Perl的隐藏特性是什么?

143

Perl 中有哪些十分实用但却比较晦涩的语言特性,你能够利用它们完成有用的工作吗?

指南:

  • 尽可能限制回答范围在 Perl 核心而非 CPAN 上
  • 请给出一个例子和简短的描述

其他编程语言中也有的隐藏特性:

(这些来自于 Corion 的回答)

  • C
    • Duff's 设备
    • 可移植性和标准性
  • C#
    • 对空格分隔列表和字符串使用引号
    • 可别名的命名空间
  • Java
    • 静态初始化器
  • JavaScript
    • 函数是一等公民
    • 块级作用域和闭包
    • 通过变量间接调用方法和访问器
  • Ruby
    • 通过代码定义方法
  • PHP
    • 无处不在的在线文档
    • 魔术方法
    • 符号引用
  • Python
    • 一行内交换值
    • 能够替换甚至是核心函数的功能实现

其他隐藏特性:

运算符:

引用结构:

语法和名称:

模块、编译指令和命令行选项:

变量:

循环和流程控制:

正则表达式:

其他特性:

其他技巧和元回答:


另请参阅:


大多数这些功能都在日常使用中,一些出现在大多数Perl脚本中,而大多数列在“其他”下的仍然源自其他语言,将它们称为“隐藏”的更改是问题意图的误解。 - reinierpost
78个回答

26

引用单词操作符是我最喜欢的操作符之一。比较如下:

my @list = ('abc', 'def', 'ghi', 'jkl');

my @list = qw(abc def ghi jkl);

Perl相比SQL来说,更加清爽易读。另一个非常好的特点是允许在最后一个元素后面加逗号,这一点在写SQL时则很难做到。

print 1, 2, 3, ;

看起来很奇怪,但如果你以另一种方式缩进代码,就不会了:

print
    results_of_foo(),
    results_of_xyzzy(),
    results_of_quux(),
    ;

在函数调用中添加一个额外的参数不需要你再调整之前或之后的逗号。单行变化不会影响上下文的其他行。

这使得使用可变参数函数非常愉快。这也许是Perl最被低估的特性之一。


2
Perl语言的一个有趣的边角情况是以下语法是有效的:for $_ qw(a list of stuff) {...} - ephemient
1
你甚至可以滥用glob语法来引用单词,只要不使用*?等特殊字符。因此,你可以编写 for (<一些东西>) { ... } - moritz
1
@ephemient:几乎。这仅适用于词法变量:对于我的 $x qw(a b c) {...} 例如:对于 $_ qw(a b c) {print} # 什么也不打印 - dland
2
@ephemient,@fengshaun,@moritz,@dland:这在blead中已经“修复”了;请参见此p5p线程 - tchrist
现在已经发布为5.14的一部分。 - Robert P
显示剩余2条评论

26

可以直接解析粘贴到数据块(DATA)中的数据。无需保存到测试文件中才能在程序中打开或类似操作。例如:

my @lines = <DATA>;
for (@lines) {
    print if /bad/;
}

__DATA__
some good data
some bad data
more good data 
more good data 

非常适用于小测试! - fengshaun
@peter mortensen 你怎么实现多个代码块?如何结束一个代码块? - Toad
@Toad:这是Allan的答案(请参见修订列表)。最好直接与该用户联系。或者,由于该用户已经离开了Stack Overflow,也可以不特定地提到任何人(以便真正的Perl专家稍后解决)。 - Peter Mortensen
3
@Hai: 不,它不是丑陋的 - 事实上,它恰好相反:干净、苗条、简约、美丽;一句话,它很棒,没有它的语言很麻烦。@peter mortensen, @toad: 在同一程序中有多个数据块的一个答案是使用CPAN上的Inline::Files模块。 - tchrist
Inline::Files 是使用源代码过滤器实现的。还有 Data::Section,它提供了多个内联块,并且不使用源代码过滤器。 - Prakash K

24

污点检测。启用污点检测后,如果尝试将污点数据(大致上是来自程序外部的数据)传递给不安全的函数(例如打开文件、运行外部命令等),Perl 将会停止执行(或者使用 -t 警告)。当编写 setuid 脚本、CGI 或任何脚本拥有比提供数据的人更高权限时,这非常有帮助。

神奇的跳转。 goto &sub 进行了优化的尾调用。

调试器。

use strictuse warnings。这些可以避免一堆拼写错误。


1
为什么其他编程语言没有这个特性?使用这个特性可以使Perl Web脚本的安全性提高一个数量级。 - Matthew Lock

24

二进制的"x"是重复操作符

print '-' x 80;     # print row of dashes

它也适用于列表:

print for (1, 4, 9) x 3; # print 149149149

这就是为什么Perl在黑客中如此受欢迎的原因之一。 perl -e 'print 0x000 x 25'; - J.J.
4
我最喜欢的用法是为 SQL INSERT 语句的最后部分生成占位符:@p = ('?') x $n; $p = join(", ", @p); $sql = "INSERT ... VALUES ($p)"; - skiphoppy

24

新增块操作

我认为,扩展语言并创建伪块操作的能力是其中之一。

  1. 您先声明一个子程序原型,指出它首先采用代码引用:

  2. sub do_stuff_with_a_hash (&\%) {
        my ( $block_of_code, $hash_ref ) = @_;
        while ( my ( $k, $v ) = each %$hash_ref ) { 
            $block_of_code->( $k, $v );
        }
    }
    
  3. 你可以在body中这样调用它

  4. use Data::Dumper;
    
    do_stuff_with_a_hash {
        local $Data::Dumper::Terse = 1;
        my ( $k, $v ) = @_;
        say qq(Hey, the key   is "$k"!);
        say sprintf qq(Hey, the value is "%v"!), Dumper( $v );
    
    } %stuff_for
    ;
    

(Data::Dumper::Dumper是另一个半隐藏的宝石。)注意到在代码块前面不需要sub关键字,也不需要哈希前面的逗号。最后看起来像这样:map {} @list

源代码过滤器

还有源代码过滤器,使用该功能可以让Perl将代码传递给你以便进行操作。不管是源代码过滤器还是块操作,都不建议在家中尝试。

我曾经用源代码过滤器做了一些很酷的东西,例如创建了一个非常简单的语言来检查时间,使得可以使用简短的Perl一行代码进行一些决策:

perl -MLib::DB -MLib::TL -e 'run_expensive_database_delete() if $hour_of_day < AM_7';

Lib::TL 将扫描变量和常量,创建它们并根据需要进行替换。

再次强调,源代码过滤器可能会很混乱,但功能强大。但是它们可能会导致调试器出现非常糟糕的问题,即使警告信息也可能打印在错误的行号上。我停止使用 Damian 的 Switch ,因为调试器失去了告诉我实际位置的所有能力。但我发现通过修改小段代码并将其保留在同一行上,可以最小化这种影响。

信号挂钩(Signal Hooks)

这通常已经足够了,但并不是那么明显。下面是一个利用旧处理程序的 die 处理程序示例:

my $old_die_handler = $SIG{__DIE__};
$SIG{__DIE__}       
    = sub { say q(Hey! I'm DYIN' over here!); goto &$old_die_handler; }
    ;

这意味着每当代码中的其他模块想要停止时,它们必须来找你(除非有人在 $SIG{__DIE__} 上进行破坏性覆盖)。而且你可以被通知有人认为某些事情是错误的。

当然,如果你只想清理一些东西,你也可以使用 END { } 块。

overload::constant

您可以在包括您的模块的包中检查特定类型的文字。例如,如果您在 import 子例程中使用此功能:

overload::constant 
    integer => sub { 
        my $lit = shift;
        return $lit > 2_000_000_000 ? Math::BigInt->new( $lit ) : $lit 
    };

如果这样做,意味着调用包中大于20亿的所有整数都将被更改为 Math::BigInt 对象(参见overload::constant)。

分组的整数字面量

顺便说一下,Perl允许您将大数字分成三位一组,并仍然获得可解析的整数。注意上面的 2_000_000_000 表示20亿。


5
当使用$SIG{DIE}处理程序时,强烈建议检查$^S以查看您的程序是否实际上正在终止,还是只是抛出将被捕获的异常。通常情况下,您不希望干扰后者。 - pjf
新的代码块非常有教育意义!我一开始还以为这是语言语义!非常感谢。 - ZeroCool

22

基于 Perl 5 中 "-n""-p" 开关的实现方式,您可以编写一个包含 }{ 的看似不正确的程序:

ls |perl -lne 'print $_; }{ print "$. Files"'

这将被内部转换为以下代码:

LINE: while (defined($_ = <ARGV>)) {
    print $_; }{ print "$. Files";
}

@martin clayton:为什么它被称为那样? - tchrist
@tchrist - 因为它看起来像两个人互相摩擦鼻子。如果你明白我的意思,在侧面看的话。 - martin clayton

18

这是一篇元回答,但Perl Tips存档包含了许多有关使用Perl做技巧的有趣技巧。以前技巧的归档在线浏览,并可通过邮件列表或Atom Feed进行订阅。

我最喜欢的一些技巧包括使用PAR构建可执行文件使用autodie自动抛出异常,以及在Perl 5.10中使用switchsmart-match结构。

披露:我是Perl Tips的作者之一和维护者,所以我显然非常推崇它们。;)


2
这可能是最好文档化的编程语言之一,并为搜索文档的工具设定了模式。因此,与其他编程语言相比,这个问题中列出的列表可能并不是必需的。 - Axeman

18

map - 不仅因为它可以使代码更具表现力,而且它激发了我对“函数式编程”进一步阅读的冲动。


18

让我们从易入难,先介绍一下太空船运算符

$a = 5 <=> 7;  # $a is set to -1
$a = 7 <=> 5;  # $a is set to 1
$a = 6 <=> 6;  # $a is set to 0

1
@Leon:C/C++对数字没有三值返回。如果我没记错,字符串比较函数是整个STL语言中唯一的三值返回函数。 据我所知,Python没有三值返回的数字比较。 Java也没有针对数字的三值返回比较。 - J.J.
7
值得一提的是,-1/0/1比较运算符有什么用处,因为并不是每个人都知道:你可以使用或运算符将它们链接在一起,进行主要/次要等排序。所以($a->lname cmp $b->lname) || ($a->fname cmp $b->fname)按照姓氏对人进行排序,但是如果两个人的姓相同,则按名字排序。 - hobbs
@J.J. Python确实有3值比较:cmp()
print (cmp(5,7), cmp(6,6), cmp(7,5)) (-1, 0, 1)
- bukzor

15

我会选择在Perl正则表达式中的 (?{}) 和 (??{}) 组。第一个组执行Perl代码,忽略返回值;第二个组执行代码,并使用返回值作为正则表达式。


Perl 发明了许多正则表达式扩展,以至于其他程序现在经常使用 PCRE(Perl 兼容正则表达式)而不是原始的正则表达式语言。 - Sec
请阅读这里的简短介绍 http://perldoc.perl.org/perlfaq6.html#Are-Perl-regexes-DFAs-or-NFAs%3f--Are-they-POSIX-compliant%3f:-D - J.J.
就我所知,Perl在正则表达式方面确实领先于其他编程语言。 - Brad Gilbert
据我所知,这个功能仍处于实验阶段,未来的 Perl 版本可能会有所不同。并不是说它没有用处,但是一个稍微更安全且同样可用的版本可以在 s/// 命令的 /e 标志中找到:s/(pattern)/reverse($1);/ge; # 反转所有 patterns - Chris Lutz
@Chris Lutz,@Leon Timmerman:请注意,这两个结构现在是可重入的。还要注意,既然我们可以在捕获组上递归,第二个结构就不必再用于实现递归模式。@Brad Gilbert:没错,尽管PCRE能够很好地跟踪我们;正则表达式的卓越性之一,Perl完全无法匹敌的是其对Unicode属性的访问权限;请参见我的unitrio分布中的uninamesunichars和尤其是uniprops,看看我所说的部分内容。 - tchrist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接