在Perl中编写宏

Laz*_*zer 13 macros perl

open $FP, '>', $outfile or die $outfile." Cannot open file for writing\n";
Run Code Online (Sandbox Code Playgroud)
  • 我的代码中有很多次这样的陈述.

  • 我想保持所有这些语句的格式相同,这样当更改某些内容时,它只会在一个地方更改.

  • 在Perl中,我该如何解决这种情况?

  • 我应该使用宏还是函数?

我看过这个SO线程如何在Perl中使用宏?,但它没有说明如何编写一般的宏

#define fw(FP, outfile) open $FP, '>', \
        $outfile or die $outfile." Cannot open file for writing\n";
Run Code Online (Sandbox Code Playgroud)

Sin*_*nür 25

首先,你应该写为:

open my $FP, '>', $outfile or die "Could not open '$outfile' for writing:$!";
Run Code Online (Sandbox Code Playgroud)

包括open失败的原因.

如果你想封装它,你可以写:

use Carp;

sub openex {
    my ($mode, $filename) = @_; 
    open my $h, $mode, $filename
        or croak "Could not open '$filename': $!";
    return $h;
}

# later

my $FP = openex('>', $outfile);
Run Code Online (Sandbox Code Playgroud)

从Perl 5.10.1开始,autodie是核心,我将第二个Chas.欧文斯建议使用它.

  • 我会说"不能"而不是"不能",因为谁知道该文件的当前状态是什么; 我们只知道当我们试图打开它时的状态(因此是过去时).当然,这是毫无意义的精确.您可以轻松地说"Bang for $ filename:$!". (2认同)
  • @Sinan:是的!!! 该部分的开头很好:"open的第一个参数可以是对文件句柄的引用.从perl 5.6.0开始,如果参数未初始化,Perl将自动创建一个文件句柄并在第一个参数中引用它"关于"它"超出范围时关闭的后一部分尚不清楚.它是什么"?如上所述,我认为这里的`openex`是该部分的一个更好的例子.请提交!我已经写了10年的Perl并且没有意识到在这种情况下你可以传递除了ref之外的任何文件.(老头,我知道......) (2认同)

Cha*_*ens 10

Perl 5真的没有宏(有源过滤器,但它们很危险,很难看,所以很丑,即使我不会把你链接到文档).函数可能是正确的选择,但您会发现它使新用户更难以阅读您的代码.一个更好的选择可能是使用autodiepragma(它是Perl 5.10.1中的核心)并且只是删除了or die部分.

如果使用Vim,另一个选择是使用snipMate.你只需输入fw<tab>FP<tab>outfile<tab>并生成

open my $FP, '>', $outfile
    or die "Couldn't open $outfile for writing: $!\n";
Run Code Online (Sandbox Code Playgroud)

snipMate文本是

snippet fw
    open my $${1:filehandle}, ">", $${2:filename variable}
        or die "Couldn't open $$2 for writing: $!\n";

    ${3}
Run Code Online (Sandbox Code Playgroud)

我相信其他编辑有类似的功能,但我是Vim用户.


daw*_*awg 7

在Perl中有几种方法可以处理类似于C宏的东西:源过滤器,子例程,Template :: Toolkit,或者在文本编辑器中使用功能.

源过滤器

如果你必须有一个C/CPP样式的预处理器宏,可以使用预编译源过滤器在Perl(或实际上,任何语言)中编写一个.您可以编写相当简单的复杂Perl类,这些类对源代码的文本进行操作,并在代码转到Perl编译器之前对其执行转换.您甚至可以直接通过CPP预处理器运行Perl代码,以获得使用Filter :: CPP在C/CPP中获得的宏扩展的确切类型.

Damian Conway的Filter :: Simple是Perl核心发行版的一部分.使用Filter :: Simple,您可以轻松编写一个简单的模块来执行您描述的宏.一个例子:

package myopinion;
# save in your Perl's @INC path as "myopinion.pm"...
use Filter::Simple;
FILTER { 
    s/Hogs/Pigs/g; 
    s/Hawgs/Hogs/g;
    }
1; 
Run Code Online (Sandbox Code Playgroud)

然后是Perl文件:

use myopinion;
print join(' ',"Hogs", 'Hogs', qq/Hawgs/, q/Hogs/, "\n");
print "In my opinion, Hogs are Hogs\n\n";
Run Code Online (Sandbox Code Playgroud)

输出:

Pigs Pigs Hogs Pigs 
In my opinion, Pigs are Pigs
Run Code Online (Sandbox Code Playgroud)

如果你重写了FILTER以替换你想要的宏,Filter :: Simple应该可以正常工作.Filter :: Simple可以限制为代码的一部分来制作变电站,例如可执行部分而不是POD部分; 只在字符串中; 只在代码中.

根据我的经验,源过滤器并未广泛使用.我大多看到他们蹩脚的尝试加密Perl源代码或幽默的Perl 混淆器.换句话说,我知道它可以通过这种方式完成,但我个人对他们的推荐不够或者说不要使用它们.

子程序

思南Ünür openex子程序是做到这一点的好办法.我只会补充一点,你会看到一个常见的旧习惯用法是将一个引用传递给类似于这样的类型:

sub opensesame {
   my $fn=shift;
   local *FH;
   return open(FH,$fn) ? *FH : undef;
}

$fh=opensesame('> /tmp/file');
Run Code Online (Sandbox Code Playgroud)

阅读perldata为什么会这样...

模板工具包

Template :: Toolkit可用于处理Perl源代码.例如,您可以按以下方式编写模板:

[% fw(fp, outfile) %] 
Run Code Online (Sandbox Code Playgroud)

通过Template :: Toolkit运行可以导致扩展和替换为:

open my $FP, '>', $outfile or die "$outfile could not be opened for writing:$!"; 
Run Code Online (Sandbox Code Playgroud)

Template :: Toolkit最常用于将凌乱的HTML和其他表示代码与Web应用程序中的应用程序代码分开.Template :: Toolkit非常积极地开发并且有很好的文档记录.如果您的唯一用途是您建议的类型的宏,则可能是过度杀伤.

文字编辑

查斯.Owens有一个使用Vim 的方法.我使用BBEdit并且可以轻松编写文本工厂来替换我想要使用的精确和不断变化的开放的开放框架.或者,您可以在"Perl"文件夹的"Resources"目录中放置一个完成模板.按下您定义的一系列键时,将使用这些完成骨架.几乎任何严肃的编辑器都具有类似的功能.

使用BBEdit,您甚至可以在文本替换逻辑中使用Perl代码.我用这种方式使用Perl :: Critic.您可以Template::Toolkit在BBEdit内部使用一些智能来处理宏.它可以设置,因此在输出要测试或编译的版本之前,模板不会更改源代码; 编辑器基本上充当预处理器.

使用文本编辑器的两个潜在问题.首先是单向/一次变换.如果你想改变你的"宏"所做的事情,你就不能这样做,因为之前的"宏"文本已经被使用了.您必须手动更改它们.第二个潜在的问题是,如果您使用模板表单,则无法将源代码的宏版本发送给其他人,因为正在编辑器中执行预处理.

不要这样做!

如果键入perl -h以获取有效的命令开关,您可能会看到一个选项:

-P                run program through C preprocessor before compilation
Run Code Online (Sandbox Code Playgroud)

诱人!是的,您可以通过C预处理器运行Perl代码并展开C样式宏并具有#defines.放下那把枪; 走开; 不要这样做.存在许多平台不兼容性和语言不兼容性.

你得到这样的问题:

#!/usr/bin/perl -P
#define BIG small
print "BIG\n";
print qq(BIG\n);
Run Code Online (Sandbox Code Playgroud)

打印:

BIG
small
Run Code Online (Sandbox Code Playgroud)

在Perl 5.12中,-P交换机已被删除......

结论

这里最灵活的解决方案就是编写一个子程序.您的所有代码都在子例程中可见,可以轻松更改,并且调用时间更短.除了代码的可读性之外,没有真正的缺点.

Template :: Toolkit被广泛使用.您可以编写复杂的替换,其行为类似于宏,甚至比C宏更复杂.如果您对宏的需求值得学习,请使用Template :: Toolkit.

对于非常简单的情况,请在编辑器中使用单向转换.

如果你真的想要C风格的宏,你可以使用Filter :: CPP.这可能与perl -P交换机具有相同的不兼容性.我不能推荐这个; 只是学习Perl方式.

如果你想在编译之前对你的代码运行Perl一个衬里​​和Perl正则表达式,请使用Filter :: Simple.

并且不要使用-P开关.无论如何,你不能在更新版本的Perl上.