Perl的隐藏特性是什么?

143

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

指南:

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

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

(这些来自于 Corion 的回答)

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

其他隐藏特性:

运算符:

引用结构:

语法和名称:

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

变量:

循环和流程控制:

正则表达式:

其他特性:

其他技巧和元回答:


另请参阅:


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

54

使用翻转操作符可跳过文件句柄返回的记录(通常为行)中的第一个迭代,而无需使用标志变量:

while(<$fh>)
{
  next if 1..1; # skip first record
  ...
}

运行perldoc perlop并搜索"flip-flop"以获取更多信息和示例。


实际上这是从 Awk 中借鉴来的,你可以通过编写 pattern1、pattern2 来在两个模式之间进行翻转。 - Bruno De Fraine
15
澄清一下,这里的“隐藏”之处在于,如果标量 '..' 的任一操作数是常量,则其值会隐式与输入行号($.)进行比较。 - Michael Carman

47

Perl 有许多不太明显的特性。

例如,您知道符号后面可以有一个空格吗?

 $ perl -wle 'my $x = 3; print $ x'
 3

如果使用符号引用,您可以为子程序赋予数字名称吗?

$ perl -lwe '*4 = sub { print "yes" }; 4->()' 
yes

还有"bool"准操作符,它返回1表示真表达式,返回空字符串表示假:

$ perl -wle 'print !!4'
1
$ perl -wle 'print !!"0 but true"'
1
$ perl -wle 'print !!0'
(empty line)

其他有趣的事情:使用use overload可以重载字符串字面量和数字(例如使它们成为BigInts或其他内容)。

其中许多内容实际上都有记录,或者是从记录的功能中逻辑推断出的,但仍然有一些不太被人所知。

更新:另一个好用的技巧。前面提到了q{...}引用构造,但您是否知道您可以使用字母作为分隔符?

$ perl -Mstrict  -wle 'print q bJet another perl hacker.b'
Jet another perl hacker.

同样,您可以编写正则表达式:

m xabcx
# same as m/abc/

2
你知道符号后面可以有空格吗?我感到非常惊讶。哇。 - Aristotle Pagaltzis
1
太棒了!!!$undef_var 不会产生警告。 - Axeman
4
我认为你使用字母来分隔字符串的例子应该是"Just another perl hacker"而不是"Jet another perl hacker" =P。 - Chris Lutz
最糟糕的是,你也可以使用其他东西作为分隔符,甚至是闭合括号。以下是有效的:s}regex}replacement}xsmg; q]string literal]; - Ryan C. Thompson

46

通过magic ARGV添加对压缩文件的支持:

s{ 
    ^            # make sure to get whole filename
    ( 
      [^'] +     # at least one non-quote
      \.         # extension dot
      (?:        # now either suffix
          gz
        | Z 
       )
    )
    \z           # through the end
}{gzcat '$1' |}xs for @ARGV;

(在文件名中使用shell元字符时,必须将$_用引号括起来)

现在<>功能将解压任何以".gz"或".Z"结尾的@ARGV文件:

while (<>) {
    print;
}

2
我认为您不需要在替换中转义 | - Chris Lutz
我正在盯着这个问题,但是我无法理解它是如何工作的。zcat | 什么时候被解析为管道命令? - Ether
1
@Ether => 检测管道是两个参数 open 的一个特性,钻石操作符在打开 @ARGV 中的每个文件时使用它。 - Eric Strom

40

在Perl中,我最喜欢的功能之一是使用布尔运算符||来在一组选择中进行选择。

 $x = $a || $b;

 # $x = $a, if $a is true.
 # $x = $b, otherwise
这意味着一个人可以写:
 $x = $a || $b || $c || 0;

$a$b$c中取第一个真值,否则取默认值0

在Perl 5.10中,还有//运算符,如果左侧有定义,则返回左侧的值,否则返回右侧的值。以下代码从$a$b$c中选择第一个已定义的值,否则取0

$x = $a // $b // $c // 0;

这些也可以使用简写形式,非常有用提供默认值:

$x ||= 0;   # 如果 $x 是假值,则它现在的值为 0。

$x //= 0;   # 如果 $x 未定义,则它现在的值为零。

祝好,

Paul


4
这个成语非常常见,几乎不能算作是一个“隐藏”的功能。 - Michael Carman
3
可惜这个漂亮的打印机认为 // 是注释 :) - John Ferguson
2
问题是,是否有“使用功能”的选项来使用这些新运算符,还是它们默认启用?我仍在学习Perl 5.10的功能。 - J.J.
6
默认情况下,其中包含该项,无需进行特殊调整。您还可以通过 dor-patch 将其移植到 5.8.x 中...请参阅任何 CPAN 镜像上的 authors/id/H/HM/HMBRAND/ 目录。 FreeBSD 6.x 及以上版本会在其 perl 软件包中为您执行此操作。 - dland
2
当将||或//与do { }结合使用时,您可以封装一个更复杂的赋值操作,例如$x = $a || do { my $z; 推导的3或4行代码; $z }; - RET
显示剩余2条评论

39

运算符++和一元运算符-不仅适用于数字,也适用于字符串。

my $_ = "a"
print -$_

打印 -a

print ++$_

打印 b

$_ = 'z'
print ++$_

输出 aa


3
引用perlvar的话: "递减操作符不是神奇的"。因此,--不能用于字符串。 - moritz
“aa” 似乎不是跟在 “z” 后面的自然元素。我会期望下一个最高的 ASCII 值,即 “{”。 - Ether
4
不要询问程序员“z”之后是什么,而要问一个人。这个功能非常适用于对长列表中的项目编号。 - Barry Brown
17
当我刚开始学习Perl时,我自己实现了这个功能,并采用了完全相同的从z到aa的行为。但后来我向一位同事展示时,他嘲笑了我并说:“让我告诉你一个东西。”虽然我有点难过,但我学到了东西。 - Copas
我希望C#也有这个功能,太棒了。 - Andrija
2
@Ether - 如果您想要这个,可以使用数字并使用 ord() 将其自动转换为 ASCII 码。或者,您也可以编写一个小的类并重载运算符来完成它。 - Chris Lutz

36
作为Perl几乎从其他列表中提取了所有“奇技淫巧”的部分,我告诉你Perl无法做到的一件事:

Perl无法在代码中具有裸露的任意URL,因为//运算符用于正则表达式。

以防您不清楚Perl提供哪些功能,这里是一个选择性列表,其中可能存在不太明显的条目:

达夫设备 - 在Perl中

可移植性和标准性 - 拥有Perl的计算机可能比拥有C编译器的计算机更多

文件/路径操作类 - File::Find适用于的操作系统甚至比.Net还要多。

引用空格分隔列表 和字符串 - Perl允许您选择几乎任意引号作为列表和字符串定界符

可别名命名空间 - Perl 通过全局变量分配实现了这些功能:

*My::Namespace:: = \%Your::Namespace

静态初始化器 - Perl 几乎可以在编译和对象实例化的每个阶段运行代码,从 BEGIN(代码解析)到 CHECK(代码解析后)到 import(模块导入时)到 new(对象实例化)到 DESTROY(对象销毁)到 END(程序退出)

函数是一等公民 - 就像在 Perl 中一样

块作用域和闭包 - Perl 都有

通过变量间接调用方法和访问器 - Perl 也可以:

my $method = 'foo';
my $obj = My::Class->new();
$obj->$method( 'baz' ); # calls $obj->foo( 'baz' )

通过代码定义方法 - Perl也允许这样做

*foo = sub { print "Hello world" };

普及的在线文档 - Perl文档在线且可能也在您的系统上

魔术方法,每当调用“不存在”的函数时调用 - Perl在AUTOLOAD函数中实现了这一点

符号引用 - 您最好远离这些。它们会吞噬你的孩子。 但是当然,Perl允许您将孩子提供给嗜血的恶魔。

一行值交换 - Perl允许列表赋值

能够替换甚至核心功能与您自己的功能

use subs 'unlink'; 
sub unlink { print 'No.' }

或者

BEGIN{
    *CORE::GLOBAL::unlink = sub {print 'no'}
};

unlink($_) for @ARGV

9
这句话的意思是:“Perl唯一无法做到的是在代码中使用裸露的任意URL,因为//运算符用于正则表达式。”这是彻头彻尾的胡说八道。 - Account deleted
为什么你会期望在任何语言中,http://example.com/是一个单一的标记,更不用说是一个字符串了?(除非它被空格和括号分隔 :)) - Hugh Allen
8
为什么/在哪里你会想要在代码中使用裸露的URL?我想不到任何例子。 - castaway
嗯,实际上 https://metacpan.org/module/Acme::URL ,看起来你可以使用裸字URL =) - Kent Fredric
显示剩余2条评论

35

1
还有一个模块可以禁用自动创建功能。 - Alexandr Ciornii
1
@Gregg Lind - 鉴于Python在首次赋值时自动创建变量,自动创建变量会因为一个小错误而导致巨大的问题。 - Chris Lutz
我发现自己松了一口气,因为这个功能被限制在Perl中。 - Omnifarious
@Omnifarious、@Chris Lutz、@Gregg Lind:自动数据创建不是一个bug而是一个特性!我向你们挑战,让你们像这样轻松地使用Python编写这个: for $i (1..10) { for $j (1 .. 10) { $a[$i][$j] = $i * $j } } - tchrist
3
@tchrist - a = [ [x*y for y in range(1,11)] for x in range(1,11) ] 这段代码创建了一个包含10个列表的列表,每个子列表包含从1到10的整数乘以该子列表所在的位置上的整数。 - Omnifarious
显示剩余4条评论

31

在Perl中引用几乎任何奇怪的字符串都很简单。

my $url = q{http://my.url.com/any/arbitrary/path/in/the/url.html};

实际上,Perl中的各种引用机制非常有趣。Perl正则表达式般的引用机制允许您引用任何东西,并指定分隔符。您可以使用几乎任何特殊字符,例如#,/或开放/关闭字符,例如(),[]或{}。 例如:

my $var  = q#some string where the pound is the final escape.#;
my $var2 = q{A more pleasant way of escaping.};
my $var3 = q(Others prefer parens as the quote mechanism.);

引用机制:

q:字面引用;只有结束字符需要转义。

qq:解释引用;处理变量和转义字符。非常适合需要引用的字符串。

my $var4 = qq{This "$mechanism" is broken.  Please inform "$user" at "$email" about it.};

qx:与qq类似,但以非交互方式执行它作为系统命令。返回从标准输出生成的所有文本。(重定向,如果在操作系统中支持,也将一并返回)。此功能也可以使用反引号(`字符)实现。

my $output  = qx{type "$path"};      # get just the output
my $moreout = qx{type "$path" 2>&1}; # get stuff on stderr too

qr: 将 qq 格式的正则表达式解释并编译为正则表达式。同时支持正则表达式的各种选项。现在你可以将正则表达式作为变量传递:

sub MyRegexCheck {
    my ($string, $regex) = @_;
    if ($string)
    {
       return ($string =~ $regex);
    }
    return; # returns 'null' or 'empty' in every context
}

my $regex = qr{http://[\w]\.com/([\w]+/)+};
@results = MyRegexCheck(q{http://myurl.com/subpath1/subpath2/}, $regex);

qw:一个非常非常有用的引用操作符。将由空格分隔的引用词语转换为列表。在单元测试中填充数据非常好用。


   my @allowed = qw(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z { });
   my @badwords = qw(WORD1 word2 word3 word4);
   my @numbers = qw(one two three four 5 six seven); # works with numbers too
   my @list = ('string with space', qw(eight nine), "a $var"); # works in other lists
   my $arrayref = [ qw(and it works in arrays too) ]; 

每当它能使事情更加清晰时,它们都很好用。对于qx、qq和q,我最有可能使用{}运算符。人们最常使用qw的习惯通常是()运算符,但有时你也会看到qw//。


1
有时我会使用 qw"",这样语法高亮器就能正确地突出显示它。 - Brad Gilbert
在SlickEdit中对我有效。 :) - Robert P
1
@fengshaun,我通常使用的编辑器可以正确地突出显示它们。 我在一定程度上是指StackOverflow上的语法高亮器。 - Brad Gilbert
@Brad Gilbert:Stack Overflow对Perl的解析不怎么样。☹ - tchrist
my $moreout = qx{type "$path" 2>&1}; ... 我不知道你可以这样做![TM] - dland

27

“for”语句可以像Pascal中的“with”一样使用:

for ($item)
{
    s/&‎nbsp;/ /g;
    s/<.*?>/ /g;
    $_ = join(" ", split(" ", $_));
}

您可以对同一变量应用一系列 s/// 操作等,而无需重复变量名称。


"map" 也能完成同样的操作... map { .... } $item;使用 "for" 相比于 "map" 的一个优势是你可以使用 next 来跳出循环。 - draegtun
2
此外,对于正在被操作的项,在执行操作的代码之前列出,可以提高可读性。 - Robert P
@RobertP:没错,话题化在语篇中非常有用。 - tchrist

27

虽然不是真的藏起来了,但许多日常使用Perl编程语言的程序员并不知道CPAN。这尤其适用于那些不是全职程序员或者不是全职使用Perl编程语言的人。


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