Perl 的隐藏特性?

Perl 中有哪些非常有用但又深奥的语言特性,您实际上已经能够使用它们来完成有用的工作?

指引:

  • 尝试将答案限制在 Perl 核心,而不是 CPAN
  • 请给出一个例子和一个简短的描述

其他语言的隐藏特征也有:

(这些都来自 Corion 的回答)

  • C
    • 达夫的装置
    • 便携性和标准化
  • C #
    • 空格分隔的列表和字符串的引号
    • 可别名命名空间
  • 爪哇咖啡
    • 静态初始化器
  • JavaScript
    • 职务是一等公民
    • 块范围和闭包
    • 通过变量间接调用方法和访问器
  • 露比
    • 通过代码定义方法
  • PHP
    • 普及的在线文档
    • 神奇的方法
    • 象征性的参考
  • 巨蟒
    • 一行值交换
    • 甚至可以用自己的功能替换核心功能

其他隐藏特征:

营办商:

引用结构:

语法和名称:

模块、 Pragmas 和命令行选项:

变量:

循环和流量控制:

正则表达式:

其他特点:

其他技巧和元答案:


参见:

61790 次浏览

让我们从简单的 宇宙飞船操作员开始。

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

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

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

$[=1;

如果您真的愿意,可以让 Perl 的行为更像 AWK(或 Fortran)。

运算符 + + 和 unary-不仅对数字有效,对字符串也有效。

my $_ = "a"
print -$_

指纹 A

print ++$_

指纹 B

$_ = 'z'
print ++$_

指纹

当循环遍历由文件句柄返回的记录(通常是行)而不使用标志变量时,触发器操作符对于跳过第一次迭代很有用:

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

运行 perldoc perlop并搜索“触发器”以获得更多信息和示例。

有点模糊的是波浪-波浪“运算符”,它强制标量上下文。

print ~~ localtime;

print scalar localtime;

不同于

print localtime;

二进制“ x”是 重复操作器重复操作器:

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

它也适用于列表:

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

根据在 Perl 5中实现 "-n""-p"交换机的方式,您可以编写一个看似不正确的程序,包括 }{:

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

它在内部被转换为这个代码:

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

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/

空文件句柄 钻石操作员<>在构建命令行工具中占有一席之地。它的作用类似于 <FH>从句柄读取,只不过它会神奇地选择最先找到的命令行文件名或 STDIN。摘自 perlop:

while (<>) {
...         # code for each line
}

特殊代码块 ,如 BEGINCHECKEND。他们来自 Awk,但在佩尔的工作方式不同,因为这里没有记录。

BEGIN块可以用来为解析阶段指定一些代码; 在执行语法和变量检查 perl -c时也会执行它。例如,要加载配置变量:

BEGIN {
eval {
require 'config.local.pl';
};
if ($@) {
require 'config.default.pl';
}
}

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$c0中选择第一个 定义值:

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

它们也可以与其简写形式一起使用,这对于提供默认值非常有用:

$x ||= 0;   # If $x was false, it now has a value of 0.


$x //= 0;   # If $x was undefined, it now has a value of zero.

再见,

保罗

m//操作符有一些模糊的特殊情况:

  • 如果使用 ?作为分隔符,它只匹配一次,除非调用 reset
  • 如果使用 '作为分隔符,则不会对模式进行内插。
  • 如果模式为空,则使用上次成功匹配的模式。

由于 Perl 拥有其他列表中几乎所有的“深奥”部分,我将告诉您 Perl 无法做到的一件事:

Perl 不能做的一件事是在代码中使用任意 URL,因为 //运算符用于正则表达式。

为了防止您不知道 Perl 提供了什么特性,下面列出了一些可能并不完全明显的条目:

Duff’s Device -< a href = “ http://perlmonks.org/? node = 388976”rel = “ nofollow noReferrer”> in Perl

可移植性和标准性 -< a href = “ http://activestate.com”rel = “ nofollow noReferrer”> 使用 Perl 的计算机可能比使用 C 编译器的计算机多

一个文件/路径操作类 -< a href = “ http://search.cpan.org/perldoc? File: : Find”rel = “ nofollow noReferrer”> File: : Find 在比.Net 更多的操作系统上工作

空格分隔列表的引号 还有绳子-< a href = “ http://perldoc.org/perlop.html”rel = “ nofollow noReferrer”> Perl 允许您为列表和字符串分隔符选择几乎任意的引号

Aliasable 名称空间 -Perl 通过 globb 分配使用这些名称空间:

*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 文档在线,可能也在你的系统上

魔法方法 ,每当您调用“ non 存在”函数时都会被调用-Perl 在 AUTOLOAD 函数中实现了这一点

符号引用 -建议你远离这些。但是当然,Perl 允许您将您的孩子提供给嗜血的恶魔。

一行值交换 -Perl 允许列表分配

甚至可以用自己的功能替换核心功能

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

或者

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


unlink($_) for @ARGV

这是一个元回答,但是 Perl Tips Perl 技巧归档包含了可以用 Perl 实现的各种有趣的技巧。以前提示的存档是在线浏览的,可以通过邮件列表或原子提要订阅。

我最喜欢的技巧包括 用 PAR 构建可执行文件使用 autodie 自动抛出异常,以及在佩尔使用 开关聪明匹配结构5.10。

披露: 我是 Perl Tips 的作者和维护者之一,所以我显然对他们评价很高。 ;)

while(/\G(\b\w*\b)/g) {
print "$1\n";
}

G 锚,是 性感

新大厦运作

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

  1. 你声明一个子节点的原型,表明它首先接受一个代码引用:

    sub do_stuff_with_a_hash (&\%) {
    my ( $block_of_code, $hash_ref ) = @_;
    while ( my ( $k, $v ) = each %$hash_ref ) {
    $block_of_code->( $k, $v );
    }
    }
    
  2. You can then call it in the body like so

    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 is another semi-hidden gem.) Notice how you don't need the sub keyword in front of the block, or the comma before the hash. It ends up looking a lot like: map { } @list

Source Filters

Also, there are source filters. Where Perl will pass you the code so you can manipulate it. Both this, and the block operations, are pretty much don't-try-this-at-home type of things.

I have done some neat things with source filters, for example like creating a very simple language to check the time, allowing short Perl one-liners for some decision making:

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

Lib::TL只需扫描“变量”和常量,根据需要创建并替换它们。

同样,源过滤器可能很混乱,但是非常强大。但是它们可能会把调试器搞得一团糟——甚至连警告都可能打印错误的行号。我停止使用 Damian 的 换人,因为调试器将失去所有能力告诉我我真正在哪里。但是我发现你可以通过修改代码的一小部分,使它们保持在同一行上,从而将损害降到最低。

信号钩子

这种情况经常发生,但并不那么明显。这是一个死亡处理程序,背负在旧的一个。

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对象。(见 常量)。

分组整数字面值

顺便说一句。Perl 允许您将大数分成三位数的组,并且仍然可以从中得到一个可解析的整数。注意: 20亿美元以上的 2_000_000_000

Axeman 提醒我,包装一些内置函数是多么容易。

在 Perl 5.10之前,Perl 没有像 Python 那样的漂亮的 print。

所以在你的本地项目中,你可以这样做:

sub print {
print @_, "\n";
}

或者添加一些调试。

sub print {
exists $ENV{DEVELOPER} ?
print Dumper(@_) :
print @_;
}

在佩尔,引用几乎任何一种奇怪的字符串都很简单。

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

事实上,Perl 中的各种引用机制非常有趣。类似于 Perlregex 的引用机制允许您引用任何内容,并指定分隔符。您几乎可以使用任何特殊字符,如 # 、/或打开/关闭字符,如()、[]或{}。例子:

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.);

引用机制:

文字引号; 只需要转义的字符是结束字符。 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//。

引号操作符是我最喜欢的东西之一。比较:

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

还有

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

少点噪音,好看点。Perl 的另一个非常好的地方是,在编写 SQL 的时候,人们很少注意到后面的逗号是合法的:

print 1, 2, 3, ;

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

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

在函数调用中添加额外的参数并不需要在前面或后面的行中使用逗号。单行更改对其周围的行没有影响。

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

检查污点。如果启用了污染检查,如果您试图将受污染的数据(粗略地说,来自程序外部的数据)传递给不安全的函数(打开文件、运行外部命令等) ,perl 将会死亡(或者使用 -t发出警告)。当编写 setuid 脚本或 CGI 或任何脚本拥有比提供数据的人更大特权的地方时,它非常有帮助。

魔法 Goto。 goto &sub做一个优化的尾部呼叫。

调试器。

use strictuse warnings。这些可以帮助你避免一系列的打字错误。

“ for”语句的使用方式与 Pascal 中使用“ with”的方式相同:

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

您可以对同一个变量应用 s///操作等序列,而不必重复变量名。

注意: 上面的不换行空格(& nbsp;)隐藏了 Unicode 来规避 Markdown,不要复制粘贴:)

rename("$_.part", $_) for "data.txt";

将 data.txt.part 重命名为 data.txt 而无需重复。

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

以及适当返回数组的版本:

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

通过 魔法 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;

(引号 $_ need 左右,用于处理包含 shell 元字符的文件名)

现在,<>特性将解压以“ . gz”或“ . Z”结尾的任何 @ARGV文件:

while (<>) {
print;
}

虽然没有真正隐藏,但是每天都有很多 Perl 程序员不知道 CPAN。这尤其适用于那些不是全职程序员或者不是佩尔全职程序员的人。

能够解析直接粘贴到 资料块中的数据。不需要保存到要在程序或类似程序中打开的测试文件。例如:

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


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

安全隔间。

使用 Safe 模块,您可以只使用 perl 构建自己的沙箱风格的环境。然后您就可以将 perl 脚本加载到沙箱中。

最好的问候,

@ Corion-Bare Perl 中的 URL?当然可以,即使是在内插的字符串中。它唯一重要的时间是在一个字符串中,您实际上将其用作正则表达式。

核心 IO::Handle模块。对我来说最重要的是它允许在文件句柄上自动刷新。例如:

use IO::Handle;
$log->autoflush(1);

通过在同一行上打印显示脚本的进度:

$| = 1; # flush the buffer on the next output


for $i(1..100) {
print "Progress $i %\r"
}

Map -不仅因为它使代码更具表现力,而且因为它给了我一种冲动,让我想多读一点关于这个“函数式编程”的知识。

使用能力如何

my @symbols = map { +{ 'key' => $_ } } @things;

从一个数组中生成一个 hashref 数组—— hashref 前面的 + 消除了块的歧义,这样解释器就知道它是一个 hashref 而不是一个代码块。太棒了。

(感谢 Dave Doyle 在上次多伦多 Perlmongers 会议上向我解释这一点。)

我不知道它有多深奥,但我最喜欢的是 大杂烩。我用它做各种各样的事情。例如,合并两个散列:

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

变量绑定接口。

循环上的继续子句。它将在每个循环的底部执行,即使是下一个循环。

while( <> ){
print "top of loop\n";
chomp;


next if /next/i;
last if /last/i;


print "bottom of loop\n";
}continue{
print "continue\n";
}

好吧。这是另一个。动态范围.我们在另一篇文章中谈到过这个问题,但是我没有在这里看到它的隐藏特征。

像自动生存这样的动态范围,使用它的语言数量非常有限

Perl 中我最喜欢的半隐藏特性是 eof函数。下面是一个非常直接来自 perldoc -f eof的示例,它展示了如何使用它轻松地跨命令行加载的多个文件重置文件名和 $.(当前行号) :

while (<>) {
print "$ARGV:$.\t$_";
}
continue {
close ARGV if eof
}

我来晚了一点,但是投票给内置的捆绑散列函数 dbmopen()——它对我帮助很大。它并不完全是一个数据库,但是如果您需要将数据保存到磁盘,它会带走很多问题,并且可以正常工作。当我没有数据库,不懂 Storable.pm 的时候,它帮助我开始,但是我知道我想要超越文本文件的阅读和写作。

Perl 循环控制构造的“绝望模式”使得它们查找堆栈以找到匹配的标签,从而允许一些令人好奇的行为,Test: : More 利用了这些行为,无论是好是坏。

SKIP: {
skip() if $something;


print "Never printed";
}


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

还有一件鲜为人知的事。私人物业管理公司档案。“ use Foo”将在 Foo.pm 之前在@INC 中查找 Foo.pmc。这是为了允许首先加载已编译的字节码,但是 模块: : 编译利用这一点来缓存源过滤模块,以便更快地加载时间和更容易的调试。

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

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

这就是我能想到的没有被提及的事情。

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

列表构造函数1. . 99或‘ a’. . ‘ zz’也非常好。

您可以用其他任何东西替换正则表达式和字符串中的分隔符。这对“倾斜牙签综合症”特别有用,例如:

$url =~ /http:\/\/www\.stackoverflow\.com\//;

您可以通过更改分隔符来消除大部分反向操作。/bar/m/bar/的简写,与 m!bar!相同。

$url =~ m!http://www\.stackoverflow\.com/!;

您甚至可以使用平衡分隔符,如{}和[]。我个人很喜欢这些。q{foo}'foo'相同。

$code = q{
if( this is awesome ) {
print "Look ma, no escaping!";
}
};

为了迷惑你的朋友(和你的语法高亮显示器) ,试试这个:

$string = qq'You owe me $1,000 dollars!';

使用 lvalue 可以让您的代码非常混乱:

my $foo = undef ;
sub bar:lvalue{ return $foo ;}


# Then later


bar = 5 ;
print bar ;

派对已经很晚了,但是: 属性。

属性实质上允许您定义与变量或子例程的声明相关联的任意代码。使用这些属性的最佳方法是使用 Attribute: : 处理程序; 这使得定义属性(还有什么,属性!)变得很容易.

我在 YAPC: : 2006,在线 给你上做了一个关于使用它们声明性地组装一个可插入类及其插件的演示。这是一个非常独特的特征。

再来一次。

Perl 缓存:

my $processed_input = $records || process_inputs($records_file);

在 Elpeleg 开放源码,Perl CMS Http://www.web-app.net/

这个不是特别有用,但是非常深奥。我在深入研究 Perl 解析器时偶然发现了这一点。

在 POD 出现之前,perl4有一个技巧,允许您将手册页作为 nroff 直接嵌入到程序中,这样它就不会丢失。Perl4使用一个名为 包装工的程序(有关详细信息,请参阅 Pink Camel 第319页)将一个 nroff 手册页巧妙地嵌入到脚本中。

它的工作方式是告诉 nroff 忽略所有代码,然后将手册页的内容放在 标记之后,该标记告诉 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 命令在 void 上下文中是字符串或数字。通常,void 上下文中的常量会产生警告。在 op.c中有一些特殊的例外情况,允许使用以某些 roff 命令开头的 void 上下文字符串。

              /* 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确实警告!

我个人很喜欢 s///操作的/e 修饰符:

while(<>) {
s/(\w{0,4})/reverse($1);/e; # reverses all words between 0 and 4 letters
print;
}

输入:

This is a test of regular expressions
^D

产出(我认为) :

sihT si a tset fo regular expressions

你可能认为你可以这样做来节省内存:

@is_month{qw(jan feb mar apr may jun jul aug sep oct nov dec)} = undef;


print "It's a month" if exists $is_month{lc $mon};

但不是这样的。Perl 仍然为每个键分配不同的标量值。偷看显示了这个。PVHV是散列表。Elt是一个键,后面的 SV是它的值。注意,每个 SV 都有一个不同的内存地址,表明它们没有被共享。

Dump \%is_month, 12;


SV = RV(0x81c1bc) at 0x81c1b0
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x812480
SV = PVHV(0x80917c) at 0x812480
REFCNT = 2
FLAGS = (SHAREKEYS)
ARRAY = 0x206f20  (0:8, 1:4, 2:4)
hash quality = 101.2%
KEYS = 12
FILL = 8
MAX = 15
RITER = -1
EITER = 0x0
Elt "feb" HASH = 0xeb0d8580
SV = NULL(0x0) at 0x804b40
REFCNT = 1
FLAGS = ()
Elt "may" HASH = 0xf2290c53
SV = NULL(0x0) at 0x812420
REFCNT = 1
FLAGS = ()

一个 undef 标量占用的内存和一个整数标量一样多,所以您可能会要求将它们全部分配给1,以避免忘记检查 exists的陷阱。

my %is_month = map { $_ => 1 } qw(jan feb mar apr may jun jul aug sep oct nov dec);


print "It's a month" if $is_month{lc $mon});
use diagnostics;

如果您正在开始使用 Perl,并且以前从未这样做过,那么这个模块将为您节省大量的时间和麻烦。对于几乎所有你能得到的基本错误信息,这个模块都会给你一个冗长的解释,告诉你为什么你的代码会崩溃,包括一些有用的提示,告诉你如何修复它。例如:

use strict;
use diagnostics;


$var = "foo";

给你这个有用的信息:

Global symbol "$var" requires explicit package name at - line 4.
Execution of - aborted due to compilation errors (#1)
(F) You've said "use strict vars", which indicates that all variables
must either be lexically scoped (using "my"), declared beforehand using
"our", or explicitly qualified to say which package the global variable
is in (using "::").


Uncaught exception from user code:
Global symbol "$var" requires explicit package name at - line 4.
Execution of - aborted due to compilation errors.
at - line 5
use diagnostics;
use strict;


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

你会看到一大段有用的文字:

syntax error at - line 5, near "};"
Execution of - aborted due to compilation errors (#1)
(F) Probably means you had a syntax error.  Common reasons include:


A keyword is misspelled.
A semicolon is missing.
A comma is missing.
An opening or closing parenthesis is missing.
An opening or closing brace is missing.
A closing quote is missing.


Often there will be another error message associated with the syntax
error giving more information.  (Sometimes it helps to turn on -w.)
The error message itself often tells you where it was in the line when
it decided to give up.  Sometimes the actual error is several tokens
before this, because Perl is good at understanding random input.
Occasionally the line number may be misleading, and once in a blue moon
the only way to figure out what's triggering the error is to call
perl -c repeatedly, chopping away half the program each time to see
if the error went away.  Sort of the cybernetic version of S.


Uncaught exception from user code:
syntax error at - line 5, near "};"
Execution of - aborted due to compilation errors.
at - line 7

从这里,您可以推断出程序可能出现的错误(在这种情况下,print 的格式完全错误)。已知的诊断错误有很多。现在,虽然在生产环境中使用它不是一件好事,但是对于那些刚接触 Perl 的人来说,它可以作为一个很好的学习帮助。

您可以使用@{[ ... ]}获得复杂 perl 表达式的内插结果

$a = 3;
$b = 4;


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

指纹: 3 * 4 = 12

山羊饲养员:

$_ = "foo bar";
my $count =()= /[aeiou]/g; #3

或者

sub foo {
return @_;
}


$count =()= foo(qw/a b c d/); #4

它之所以能够工作,是因为标量上下文中的列表分配会产生被分配的列表中的元素数。

注意,不是真正的操作员

可以将输入记录分隔符设置为对一个数字的引用,以读取固定长度的记录:

$/ = \3; print $_,"\n" while <>; # output three chars on each line

下面的代码与“ ~ ~”一样简短,但更有意义,因为它们指示返回的内容,而且与智能匹配操作符没有混淆:

print "".localtime;   # Request a string


print 0+@array;       # Request a number

Quantum::Superpositions

use Quantum::Superpositions;


if ($x == any($a, $b, $c)) { ...  }

Schwartzian 变换是一种技术,它允许您通过计算的辅助索引进行高效排序。假设您希望根据字符串的 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 次 logN 次。

$0是正在执行的 perl 脚本的名称。它可以用来获取运行模块的上下文。

# MyUsefulRoutines.pl


sub doSomethingUseful {
my @args = @_;
# ...
}


if ($0 =~ /MyUsefulRoutines.pl/) {
# someone is running  perl MyUsefulRoutines.pl [args]  from the command line
&doSomethingUseful (@ARGV);
} else {
# someone is calling  require "MyUsefulRoutines.pl"  from another script
1;
}

这个习惯用法有助于处理带有一些有用子例程的独立脚本,这些子例程可以导入到其他脚本的库中。Python 具有与 object.__name__ == "__main__"习惯用法类似的功能。

如果程序在调试器内运行,则表达式 defined &DB::DB返回 true。

有条件地将字符串或列表添加到其他列表中的一个有用的复合操作符是 x!!操作符:

 print 'the meaning of ', join ' ' =>
'life,'                x!! $self->alive,
'the universe,'        x!! ($location ~~ Universe),
('and', 'everything.') x!! 42; # this is added as a list

此运算符允许类似于

 do_something() if test();

匹配正则表达式的插值。这种方法的一个有用的应用程序是在黑名单上进行匹配时。不使用插值,它是这样写的:

#detecting blacklist words in the current line
/foo|bar|baz/;

可以被写下来

@blacklistWords = ("foo", "bar", "baz");
$anyOfBlacklist = join "|", (@blacklistWords);
/$anyOfBlacklist/;

这更加详细,但是允许从数据文件填充。此外,如果出于某种原因在源代码中维护列表,那么维护数组比维护 RegExp 更容易。

使用散列(其中键是唯一的)来获得列表的唯一元素:

my %unique = map { $_ => 1 } @list;
my @unique = keys %unique;

使用带有 redo或其他控制词的裸块创建自定义循环结构。

遍历一个返回第一个 ->can('print')方法的对象链表:

sub get_printer {
my $self = shift;
{$self->can('print') or $self = $self->next and redo}
}

为 unpack ()和 pack ()函数添加一个函数,如果需要以其他程序使用的格式导入和/或导出数据,这样做非常好。

当然,现在大多数程序都允许导出 XML 格式的数据,而且许多常用的专有文档格式都有相关的 Perl 模块。但是,当您需要它时,这是非常有用的特性之一,pack ()/unpack ()可能就是 原因,人们已经能够为如此多的专有数据格式编写 CPAN 模块。

有一种更强大的方法来检查程序的语法错误:

perl -w -MO=Lint,no-context myscript.pl

它能做的最重要的事情是报告“不存在的子程序”错误。

use re debug
文件正在使用重新调试

还有

perl -MO=Concise[,OPTIONS]
简明博士

除了非常灵活、富有表现力和易于使用 C、 Pascal、 Python 和其他语言的风格进行编程之外,还有一些实用的命令开关,它们使 Perl 成为我的“ goto”语言,可以用来处理算法、正则表达式或需要解决的快速问题。我相信这两个对 Perl 来说是独一无二的,也是我的最爱之一。

返回文章页面 大多数现代风格的正则表达式都将其当前的形式和功能归功于 Perl。虽然有许多 Perl 形式的正则表达式不能在其他语言中表达,但是几乎没有其他语言形式的正则表达式不能在 Perl 中表达。此外,Perl 还内置了一个很棒的正则表达式调试器,可以显示正则表达式引擎如何解释您的正则表达式并与目标字符串进行匹配。

例如: 我最近正在尝试编写一个简单的 CSV 例程。(是的,是的,我知道,我应该使用 文本: : CSV..。) ,但 CSV 值没有引用和简单。

我的第一次采取是 /^(^(?:(.*?),){$i}/提取的 i 记录的 n CSV 记录。这样工作得很好——除了最后一条记录或 n。

接下来我尝试 /^(?:(.*?),|$){$i}/这不工作,我不能立即看到为什么。我以为我说的是 (.*?)后面跟个逗号或者 EOL。然后,我在一个小测试脚本的顶部添加了 use re debug。啊,是的,,|$之间的改变没有被这样解释; 它被解释为 ((.*?),) | ($)——不是我想要的。

需要一个新的分组。所以我来到了工作的 /^(?:(.*?)(?:,|$)){$i}/。当我在正则表达式调试器中时,我惊讶地发现在字符串末尾处匹配需要多少个循环。.*?这个术语非常模糊,需要过多的回溯才能满足。因此,我尝试了 /^(?:(?:^|,)([^,]*)){$i}/。这样做有两个好处: 1)减少回溯,因为除了逗号之外,所有的回溯都是贪婪的。2)允许正则表达式优化器在第一个字段只使用一次更改。使用 Benchmark,这比第一个正则表达式快35% 。正则表达式调试器非常棒,很少有人使用它。

返回文章页面 B 和 Conise 框架是了解 Perl 如何解释您的杰作的非常好的工具。使用 -MO=Concise打印源代码的 Perl 解释器翻译结果。有许多选项简洁和在 B 中,您可以编写自己的 OP 代码表示。

作为 在这个职位,您可以使用简洁来比较不同的代码结构。您可以将源代码行与这些代码行生成的 OP 代码交织在一起。看看这个。

有两样东西可以很好地协同工作: 对内核字符串的 IO 处理,以及使用函数原型使您能够使用 grep/map 类似的语法编写自己的函数。

sub with_output_to_string(&) {           # allows compiler to accept "yoursub {}" syntax.
my $function = shift;
my $string   = '';
my $handle   = IO::Handle->new();
open($handle, '>', \$string) || die $!; # IO handle on a plain scalar string ref
my $old_handle = select $handle;
eval { $function->() };
select $old_handle;
die $@ if $@;
return $string;
}


my $greeting = with_output_to_string {
print "Hello, world!";
};


print $greeting, "\n";

Perl 非常适合作为灵活的 awk/sed。

例如,让我们使用一个简单的 ls | xargs stat替代品,比如:

$ ls | perl -pe 'print "stat "' | sh

当输入(文件名)具有空格或 shell 特殊字符(如 |$\)时,这种方法不能很好地工作。因此,在 Perl 输出中经常需要单引号。

通过命令行 -ne调用 perl 的一个复杂情况是 shell 首先会蚕食您的一行程序。这常常导致为了满足它而痛苦地逃跑。

我一直使用的一个“隐藏”特性是 \x27包含一个引号,而不是尝试使用 shell 转义 '\''

所以:

$ ls | perl -nle 'chomp; print "stat '\''$_'\''"' | sh

可以更安全地写:

$ ls | perl -pe 's/(.*)/stat \x27$1\x27/' | sh

这对文件名中的滑稽角色不起作用,即使是这样引用。但是这个可以:

$ ls | perl -pe 's/\n/\0/' | xargs -0 stat

下次你参加极客派对的时候,拿出这句俏皮话,女人们就会蜂拥而至,你的朋友们也会崇拜你:

- name“ * . txt”| xargs perl-pi-e’s/1: (S +)/uc ($1)/ge’

处理所有 * 。并使用 perl 的 regex 执行就地查找和替换操作。这个函数将“1:”后面的文本转换为大写,并删除“1:”。使用 Perl 的‘ e’修饰符将 find/place regex 的第二部分视为可执行代码。即时一行模板系统。使用 xargs 可以处理大量文件,而不会遇到 bash 的命令行长度限制。

在循环中使用哈希作为可见过滤器的能力。我还没见过用另一种语言写的这么好的东西。例如,我无法在 python 中复制这一点。

例如,如果以前没有看到过,我想打印一行。

my %seen;


for (<LINE>) {
print $_ unless $seen{$_}++;
}

命令行上的 new-E 选项:

> perl -e "say 'hello"" # does not work


String found where operator expected at -e line 1, near "say 'hello'"
(Do you need to predeclare say?)
syntax error at -e line 1, near "say 'hello'"
Execution of -e aborted due to compilation errors.


> perl -E "say 'hello'"
hello

例如,可以展开字符串中的函数调用;

print my $foo = "foo @{[scalar(localtime)]} bar";

Foo Wed May 2615:50:302010 bar

您可以在 HEREDOCS 上使用不同的引号来获得不同的行为。

my $interpolation = "We will interpolated variables";
print <<"END";
With double quotes, $interpolation, just like normal HEREDOCS.
END


print <<'END';
With single quotes, the variable $foo will *not* be interpolated.
(You have probably seen this in other languages.)
END


## this is the fun and "hidden" one
my $shell_output = <<`END`;
echo With backticks, these commands will be executed in shell.
echo The output is returned.
ls | wc -l
END


print "shell output: $shell_output\n";

@ Schwern 提到了通过本地化 $SIG{__WARN__}将警告转化为错误。

关于这一点,自从 Perl 5.12以来,您已经能够说 perldoc foo而不是完整的 perldoc perlfoo

“现在”

sub _now {
my ($now) = localtime() =~ /([:\d]{8})/;
return $now;
}


print _now(), "\n"; #  15:10:33

我最喜欢的特性是语句修饰符。

不知道我想过多少次:

say 'This will output' if 1;
say 'This will not output' unless 1;
say 'Will say this 3 times. The first Time: '.$_ for 1..3;

翻译成其他语言。 等等。

“ etc”让我想起了另一个5.12特性,Yada Yada 操作符。

这是伟大的,当你只是想要一个位置持有人的时候。

sub something_really_important_to_implement_later {
...
}

看看 Yada Yada 操作符上的 Perl 文档

下面的一行程序演示了如何使用 globb 为指定长度的单词(4)生成字母表(A、 T、 C 和 G-> DNA)的所有单词组合:

perl -MData::Dumper -e '@CONV = glob( "{A,T,C,G}" x 4 ); print Dumper( \@CONV )'

我喜欢在数组中的任何位置插入元素的方式,比如

= > 在 array@a 中的 $i 位置插入 $x

@a = ( 11, 22, 33, 44, 55, 66, 77 );
$x = 10;
$i = 3;


@a = ( @a[0..$i-1], $x, @a[$i..$#a] );

Deparse -Perl 编译器后端生成 Perl 代码。您不会在日常的 Perl 编码中使用它,但是在特殊情况下可能会很有用。

如果遇到一些混淆的代码片段,或者一个复杂的表达式,通过 Deparse传递它。有助于指出一个 JAPH 或 Perl 代码是高尔夫球。

$ perl -e '$"=$,;*{;qq{@{[(A..Z)[qq[0020191411140003]=~m[..]g]]}}}=*_=sub{print/::(.*)/};$\=$/;q<Just another Perl Hacker>->();'
Just another Perl Hacker


$ perl -MO=Deparse -e '$"=$,;*{;qq{@{[(A..Z)[qq[0020191411140003]=~m[..]g]]}}}=*_=sub{print/::(.*)/};$\=$/;q<Just another Perl Hacker>->();'
$" = $,;
*{"@{[('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')['0020191411140003' =~ /../g]];}";} = *_ = sub {
print /::(.*)/;
}
;
$\ = $/;
'Just another Perl Hacker'->();
-e syntax OK

一个更有用的示例是使用 parse 查找 coderef 背后的代码,这些代码可能来自另一个模块,或者

use B::Deparse;
my $deparse = B::Deparse->new;
$code = $deparse->coderef2text($coderef);
print $code;