Perl的隐藏特性是什么?

143

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

指南:

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

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

(这些来自于 Corion 的回答)

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

其他隐藏特性:

运算符:

引用结构:

语法和名称:

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

变量:

循环和流程控制:

正则表达式:

其他特性:

其他技巧和元回答:


另请参阅:


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

9
Perl循环控制结构的“绝望模式”会导致它们查找堆栈以找到匹配的标签,这会带来一些奇怪的行为,Test::More利用了这些行为,好或坏都有可能。
SKIP: {
    skip() if $something;

    print "Never printed";
}

sub skip {
    no warnings "exiting";
    last SKIP;
}

有一个鲜为人知的 .pmc 文件。"use Foo" 会在 Foo.pm 前在 @INC 中查找 Foo.pmc。这是为了允许编译后的字节码优先加载,但 Module::Compile 利用此功能缓存源代码过滤模块以提高加载速度和更容易调试。

将警告转换为错误的能力。

local $SIG{__WARN__} = sub { die @_ };
$num = "two";
$sum = 1 + $num;
print "Never reached";

这是我能够想到的还没有被提到的内容。


7
你可以使用@{[...]}来获取复杂Perl表达式的插值结果。
$a = 3;
$b = 4;

print "$a * $b = @{[$a * $b]}";

打印输出:3 * 4 = 12


7

我不知道这是否深奥,但我最喜欢的之一是哈希切片。我用它来做各种事情。例如合并两个哈希表:

my %number_for = (one => 1, two => 2, three => 3);
my %your_numbers = (two => 2, four => 4, six => 6);
@number_for{keys %your_numbers} = values %your_numbers;
print sort values %number_for; # 12346

%number_for = (%number_for, %your_numbers); - ijw

7

这个技巧并不是特别有用,但它非常深奥。我在Perl解析器中挖掘时偶然发现了这个技巧。

在POD出现之前,perl4有一个诀窍可以让你将man页作为nroff直接嵌入程序中,这样就不会丢失。Perl4使用了一个叫做wrapman(详见《Pink Camel page 319》)的程序来巧妙地将nroff man页嵌入到脚本中。

它的原理是告诉nroff忽略所有代码,然后把man页的核心内容放在END标记后面,告诉Perl停止处理代码。看起来有点像这样:

#!/usr/bin/perl
'di';
'ig00';

...Perl code goes here, ignored by nroff...

.00;        # finish .ig

'di         \" finish the diversion
.nr nl 0-1  \" fake up transition to first page
.nr % 0     \" start at page 1
'; __END__

...man page goes here, ignored by Perl...

roff 魔术的具体细节我无法回忆起来,但你会注意到 roff 命令在空上下文中是字符串或数字。通常情况下,在空上下文中使用常量会产生警告。在 op.c 中有特殊的例外,允许以某些 roff 命令开头的字符串出现在空上下文中。
              /* perl4's way of mixing documentation and code
                 (before the invention of POD) was based on a
                 trick to mix nroff and perl code. The trick was
                 built upon these three nroff macros being used in
                 void context. The pink camel has the details in
                 the script wrapman near page 319. */
                const char * const maybe_macro = SvPVX_const(sv);
                if (strnEQ(maybe_macro, "di", 2) ||
                    strnEQ(maybe_macro, "ds", 2) ||
                    strnEQ(maybe_macro, "ig", 2))
                        useless = NULL;

这意味着'di';不会产生警告,但'die';'did you get that thing I sentcha?';'ignore this line';也同样不会。此外,对于数字常量01也有例外,允许裸的.00;。代码声称这是为了更普遍的目的。
            /* the constants 0 and 1 are permitted as they are
               conventionally used as dummies in constructs like
                    1 while some_condition_with_side_effects;  */
            else if (SvNIOK(sv) && (SvNV(sv) == 0.0 || SvNV(sv) == 1.0))
                useless = NULL;

你知道吗,2 while condition确实会出现警告!


这确实是Perl的一个隐藏特性! - dolmen

6
use diagnostics;

如果你开始使用Perl并且以前从未使用过,这个模块将为你节省大量的时间和麻烦。对于几乎每个基本的错误消息,这个模块都会给出一个详细的解释,说明为什么你的代码出错了,还包括一些有用的提示,告诉你如何修复它。例如:

use strict;
use diagnostics;

$var = "foo";

为您提供这个有用的信息:

全局符号“$var”在第4行需要显式地指定包名称。
由于编译错误(#1),执行被中止
    (F)您已经使用了“use strict vars”,这表明所有变量
    必须是词法作用域的(使用“my”),预先声明了
    “our”,或明确限定以说明全局变量所在的包(使用“::”)。
用户代码的未捕获异常: 全局符号“$var”在第4行需要显式地指定包名称。 由于编译错误,- 的执行中止。 在第5行 -
use diagnostics;
use strict;

sub myname {
    print { " Some Error " };
};

您得到了这个大而有用的文本块:
语法错误在第5行附近,“};”
执行中止由于编译错误(#1)
(F)可能意味着您有语法错误。常见原因包括:
关键字拼写错误。 缺少分号。 缺少逗号。 缺少开放或关闭括号。 缺少开放或关闭大括号。 缺少闭合引号。
通常,与语法错误相关联的还会有另一个错误消息,提供更多信息。(有时打开-w会有所帮助。)错误消息本身通常会告诉您何时决定放弃它所在的行。有时实际错误是在此之前几个令牌,因为Perl善于理解随机输入。偶尔,行号可能会误导,并且有时候唯一确定触发错误的方法是反复调用perl -c,每次切掉程序的一半,以查看错误是否消失。有点像S的仿生版本。
用户代码未捕获的异常: 语法错误在第5行附近,“};” 执行中止由于编译错误。 位于第7行
从那里,您可以推断出您的程序可能存在什么问题(在本例中,print的格式完全错误)。已知有许多诊断错误。现在,虽然这不是在生产中使用的好东西,但它可以作为一个很好的学习辅助工具,特别是对于那些刚接触Perl的人来说。

6
sub load_file
{
    local(@ARGV, $/) = shift;
    <>;
}

并提供返回适当数组的版本:

sub load_file
{
    local @ARGV = shift;
    local $/ = wantarray? $/: undef;
    <>;
}

5

@Schwern提到通过本地化$SIG{__WARN__}将警告转换为错误。您也可以使用use warnings FATAL => "all";(词法上)来实现这一点。请参见perldoc lexwarn

顺便说一下,自Perl 5.12以来,您可以使用perldoc foo而不是完整的perldoc perlfoo。终于! :)


5

($x, $y) = ($y, $x)是让我想学习Perl的原因。

列表生成器1..99或'a'..'zz'也很好用。


5

还有一个变量决定数组从哪个索引开始。默认为0,因此数组从0开始。通过设置

$[=1;

如果你真的想要,你可以让Perl更像AWK(或Fortran)。


3
尽管引用perlvar文件中的话说:“高度不建议使用它。”但并不多见有人期望数组的起始下标会改变。 - pjf
2
如果有需要,我只会在一行代码中使用这个功能。 - Brad Gilbert
警告:如果你在一个CPAN模块中这样做,chromatic会找到你并喷漆你的车。(这是一个玩笑;没有咨询chromatic。) - davidnicol

4

Schwartzian Transform 是一种技术,可以通过计算二级索引来高效地排序。比如说你想按照md5值对字符串列表进行排序。下面的注释最好倒着读(我总是以这种方式编写):

my @strings = ('one', 'two', 'three', 'four');

my $md5sorted_strings = 
    map { $_->[0] }               # 4) map back to the original value
    sort { $a->[1] cmp $b->[1] }  # 3) sort by the correct element of the list
    map { [$_, md5sum_func($_)] } # 2) create a list of anonymous lists
    @strings                      # 1) take strings

这样,您只需要进行昂贵的md5计算N次,而不是N log N次。

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