Perl的隐藏功能?
我们实际上已能够使用Perl中哪些真正有用但深奥的语言功能来完成有用的工作?
指导原则:
- 尝试将答案限制在Perl核心而非CPAN上
- 请举一个例子和简短的描述
在其他语言的"隐藏功能"中也可以找到"隐藏功能":
(这些全都来自Corion的回答)
- 便携性和标准性
- 别名空间
- 块范围和闭包
- 通过变量间接调用方法和访问器
- 魔术方法
- 符号参考
- 能够用自己的功能替换甚至核心功能
其他隐藏功能:
运营商:
- 布尔准运算符
++
和一元-
运算符适用于字符串- 重复运算符
- 太空飞船的运营商
- ||运算符(和/或者运算符)以从一组选项中进行选择
- 钻石操作员
m //
运算符的特殊情况- 代字号-代字号"运算符"
引用构造:
- qw运算符
- 字母可用作类似q {}的结构中的引号定界符
- 报价机制
语法和名称:
- 标记后可以有空格
- 我们可以给subs数字名称加上符号引用
- 法律尾部逗号
- 分组整数文字
- 哈希片
- 从数组填充哈希键
模块,编译指示和命令行选项:
- 使用严格并使用警告
- 污点检查
- -n和-p的深奥用法
- CPAN
overload :: constant
- IO :: Handle模块
- 安全隔层
- 属性
变量:
- 自我生存
$ [
变量- 领带
- 动态范围
- 单个语句进行变量交换
循环和流量控制:
- 魔术转到
- 对于单个变量
- 继续条款
- 拼版模式
常用表达:
- \ G锚点
- 正则表达式中的((?{})`和'(?? {}))
其它功能:
- 调试器
- 特殊代码块,例如BEGIN,CHECK和END
- DATA块
- 新区块操作
- 源过滤器
- 信号钩
- 地图(两次)
- 包装内置功能
eof
函数- dbmopen函数
- 把警告变成错误
其他技巧和元答案:
- cat文件,必要时解压缩gzip
- Perl提示
也可以看看:
- C的隐藏功能
- C#的隐藏功能
- C ++的隐藏功能
- Java的隐藏功能
- JavaScript的隐藏功能
- Ruby的隐藏功能
- PHP的隐藏功能
- Python的隐藏功能
- Clojure的隐藏功能
解决方案
让我们从太空船操作员开始轻松。
$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)。
运算符++和一元运算符不仅适用于数字,而且适用于字符串。
my $_ = "a" print -$_
打印-a
print ++$_
打印b
$_ = 'z' print ++$_
打印aa
触发器运算符在循环遍历文件句柄返回的记录(通常为行)时不使用标志变量时,可用于跳过第一次迭代:
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
还是如果我们使用符号引用,可以给subs数字名称?
$ 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)
其他有趣的东西:使用"使用重载"可以重载字符串文字和数字(例如,使它们成为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 }
特殊代码块,例如" BEGIN"," CHECK"和" END"。它们来自Awk,但在Perl中的工作方式有所不同,因为它不是基于记录的。
" 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,$ c或者0中选择第一个定义的值:
$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在Perl中的设备
可移植性和标准性与Perl C编译器相比,使用Perl的计算机可能更多
文件/路径操作类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
这是一个元答案,但是Perl技巧档案包含了Perl可以完成的各种有趣的技巧。先前技巧的存档可以在线浏览,并且可以通过邮件列表或者atom feed进行订阅。
我最喜欢的一些技巧包括使用PAR构建可执行文件,使用autodie自动引发异常以及在Perl 5.10中使用switch和smart-match结构。
披露:我是Perl Tips的作者和维护者之一,因此,我显然对他们非常重视。 ;)
自我生存。 AFAIK没有其他语言。
while(/\G(\b\w*\b)/g) { print "\n"; }
\ G锚点。很热。
新区块操作
我想说,扩展语言,创建伪块操作的能力是其中之一。
- 我们为一个子程序声明原型,以指示该子程序首先接受代码引用:
sub do_stuff_with_a_hash (&\%) { my ( $block_of_code, $hash_ref ) = @_; while ( my ( $k, $v ) = each %$hash_ref ) { $block_of_code->( $k, $v ); } }
- 然后我们可以像这样在体内称呼它
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,因为调试器将失去所有告诉我实际位置的能力。但是我发现,可以通过修改一小段代码并将它们保持在同一行上来最大程度地减少损害。
信号钩
通常这已经足够完成了,但是还不是很明显。这是一个模子处理程序,背负着旧的处理程序。
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允许我们将大数字分成三位数的组,并且仍然可以从中得到一个可解析的整数。注意上面的" 2_000_000_000"为20亿。
Axeman提醒我包装一些内置函数很容易。
在Perl 5.10之前,Perl没有像Python这样漂亮的外观(例如)。
因此,在本地程序中,我们可以执行以下操作:
sub print { print @_, "\n"; }
或者添加一些调试。
sub print { exists $ENV{DEVELOPER} ? print Dumper(@_) : print @_; }
在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 //。
引号运算符是我最喜欢的东西之一。比较:
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&sub
进行了优化的尾部调用。
调试器。
"使用严格"和"使用警告"。这些可以避免一堆错字。
可以与在Pascal中使用" with"的方式相同,使用" for"语句:
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 '' |}xs for @ARGV;
(用外壳元字符处理文件名时,必须在$ _前后加上引号)
现在<>
功能将解压缩以" .gz"或者" .Z"结尾的所有@ ARGV
文件:
while (<>) { print; }
并不是真正的隐藏,但是许多Perl程序员每天都不了解CPAN。这尤其适用于那些不是全职程序员或者不以Perl全职编程的人。
解析直接粘贴到DATA块中的数据的能力。无需保存到要在程序中打开的测试文件或者类似文件。例如:
my @lines = <DATA>; for (@lines) { print if /bad/; } __DATA__ some good data some bad data more good data more good data
安全隔间。
使用安全模块,我们可以仅使用perl来构建自己的沙盒式环境。然后,我们将能够将perl脚本加载到沙箱中。
最好的祝福,
Perl中的@Corion裸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" }
映射不仅是因为它使人的代码更具表现力,而且还因为它带给了我阅读更多有关此"函数式编程"的动力。
使用能力怎么样
my @symbols = map { +{ 'key' => $_ } } @things;
从数组生成哈希引用数组-哈希引用前面的+消除了该块的歧义,因此解释器知道它是哈希引用而非代码块。惊人的。
(感谢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
tie,可变绑定接口。
循环上的continue子句。它会在每个循环的底部执行,即使是下一个循环也是如此。
while( <> ){ print "top of loop\n"; chomp; next if /next/i; last if /last/i; print "bottom of loop\n"; }continue{ print "continue\n"; }
好的。这是另一个。动态范围。在另一篇文章中对此进行了讨论,但是在隐藏的功能中我没有看到它。
像Autovivification这样的动态范围,使用它的语言数量非常有限。我只知道Perl和Common Lisp使用动态范围。
我最喜欢的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; }
有鲜为人知的.pmc文件。 "使用Foo"将在Foo.pm之前在@INC中查找Foo.pmc。这样做的目的是允许首先加载已编译的字节码,但是Module :: Compile可以利用此优势来缓存经过源过滤的模块,以加快加载时间并简化调试。
将警告变成错误的能力。
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 ,000 dollars!';
使用左值使代码真正令人困惑:
my $foo = undef ; sub bar:lvalue{ return $foo ;} # Then later bar = 5 ; print bar ;
参加聚会很晚,但是:属性。
本质上,属性使我们可以定义与变量或者子例程的声明关联的任意代码。最好的使用方法是使用Attribute :: Handlers;。这样可以轻松定义属性(就属性而言!)。
我在网上的YAPC :: 2006上做了一个有关使用它们声明性地组装可插拔类及其插件的演示。这是一个非常独特的功能。
多一个...
Perl缓存:
my $processed_input = $records || process_inputs($records_file);
在埃尔佩莱格
开源,Perl CMS
http://www.web-app.net/
这不是特别有用,但是非常深奥。我在Perl解析器中闲逛时偶然发现了这一点。
在出现POD之前,perl4有一个技巧,允许我们将手册页(如nroff)直接嵌入程序中,以免丢失。 perl4使用了一个称为wrapman的程序(有关某些详细信息,请参见Pink Camel的第319页),将nroff手册页巧妙地嵌入到脚本中。
它通过告诉nroff忽略所有代码,然后将手册页的内容放在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';``'也不会发出我发来的东西吗?';
或者'忽略此行';`。
另外,数字常量" 0"和" 1"也有例外,它们允许使用裸露的" .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();/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仍然为每个键分配一个不同的标量值。 Devel :: Peek显示了这一点。 PVPV是哈希值。 " 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
从那里,我们可以推断出程序可能存在的问题(在这种情况下,打印格式完全错误)。诊断中存在大量已知的错误。现在,虽然这在生产中不是一件好事,但对于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
之所以有效,是因为标量上下文中的列表分配会产生要分配的列表中元素的数量。
*
注意,不是真正的运算符
可以将输入记录分隔符设置为对数字的引用,以读取固定长度的记录:
$/ = ; 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
这样,我们只需执行N次昂贵的md5计算,而不需要进行N次log N次。
$ 0是正在执行的perl脚本的名称。它可用于获取从中运行模块的上下文。
# MyUsefulRoutines.pl sub doSomethingUseful { my @args = @_; # ... } if (print 'the meaning of ', join ' ' => 'life,' x!! $self->alive, 'the universe,' x!! ($location ~~ Universe), ('and', 'everything.') x!! 42; # this is added as a list=~ /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 !!
运算符:
do_something() if test();
此运算符允许使用与以下类似的相反语法
#detecting blacklist words in the current line /foo|bar|baz/;
匹配正则表达式的插值。当在黑名单上进行匹配时,这是一个有用的应用程序。在不使用插值的情况下,其编写方式如下:
@blacklistWords = ("foo", "bar", "baz"); $anyOfBlacklist = join "|", (@blacklistWords); /$anyOfBlacklist/;
可以改写为
my %unique = map { $_ => 1 } @list; my @unique = keys %unique;
这比较冗长,但允许从数据文件进行填充。同样,如果出于某种原因在源中维护列表,则与RegExp相比,维护数组更容易。
使用哈希(键是唯一的)获得列表的唯一元素:
sub get_printer { my $self = shift; {$self->can('print') or $self = $self->next and redo} }
使用带有" redo"或者其他控制字的裸露块来创建自定义循环结构。
遍历返回第一个`-> can('print')方法的对象的链接列表:
perl -w -MO=Lint,no-context myscript.pl
为unpack()和pack()函数添加一个,如果我们需要以其他程序使用的格式导入和/或者导出数据,则这非常有用。
当然,如今,大多数程序都允许我们以XML格式导出数据,并且许多常用的专有文档格式都为它们编写了相关的Perl模块。但这是当我们需要时非常有用的那些功能之一,而pack()/ unpack()可能是人们能够为许多专有数据格式编写CPAN模块的原因。
有一种更强大的方法来检查程序中的语法错误:
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 -MO =简明[,OPTIONS]
简明文档
除了以C,Pascal,Python和其他语言的风格非常灵活,表现力强并且易于编程外,还有一些编译指示命令开关使Perl成为我的" goto"语言,可用于对算法,正则表达式或者其他快速问题进行初步确定需要解决。我相信这两个是Perl独有的,并且是我的最爱。
使用重新调试
:
正则表达式的大多数现代风格都归功于Perl的当前形式和功能。尽管有许多Perl形式的regex无法用其他语言表达,但是几乎没有其他形式的regex形式无法以Perl表达。此外,Perl内置了一个很棒的regex调试器,以显示regex引擎如何解释regex并与目标字符串进行匹配。
示例:我最近试图编写一个简单的CSV例程。 (是的,是的,我知道,我应该一直在使用Text :: CSV ...),但是CSV值没有被引用并且很简单。
我的第一个选择是/^(^(?:(.*?),){$ i} /
来提取n个CSV记录中的i记录。可以看到没有调试器。
接下来,我尝试了/^(?:(.*?),| $){$ i} /
,这没有用,我也看不到为什么。我以为我是在说(。*?)
,然后是逗号或者EOL。然后我在一个小的测试脚本的顶部添加了" use re debug"。嗯,是的,,| $
之间的变化不是用这种方式解释的;它被解释为((.. ??),)| ($)
-不是我想要的。
需要一个新的分组。因此,我到达了工作中的/ ^(?:(。*?)(?:,| $)){$ i} /
。当我在正则表达式调试器中时,令我惊讶的是,在字符串末尾进行匹配时要花多少个循环。 "。*?"这个术语含糊不清,需要过多的回溯才能满足。所以我尝试了/ ^(?:(?:^ |,)([^,] *)){$ i} /
这可以做两件事:1)减少回溯,因为除了逗号以外的所有人都贪婪匹配2 )允许regex优化器仅在第一个字段上使用一次更改。使用Benchmark,这比第一个正则表达式快35%。 regex调试器很棒,很少使用它。
perl -MO =简明[,OPTIONS]
:
B和Concise框架是查看Perl如何解释杰作的绝佳工具。使用-MO = Concise
可以打印源代码的Perl解释器翻译结果。简洁有很多选项,在B中,我们可以编写自己的OP代码表示。
如本文所述,我们可以使用Concise比较不同的代码结构。我们可以将源代码行与这些代码行生成的OP代码进行交织。一探究竟。
可以很好地协同工作的两件事:IO处理内核字符串,并使用函数原型使我们能够使用类似grep / map的语法编写自己的函数。
$ ls | perl -pe 'print "stat "' | sh
Perl可以作为灵活的awk / sed很好。
例如,让我们使用ls |的简单替换。 xargs stat
,像这样天真地完成:
$ ls | perl -nle 'chomp; print "stat '\''$_'\''"' | sh
当输入(文件名)包含空格或者shell特殊字符(如| $ \
)时,此方法将无法正常工作。因此,Perl输出中经常需要单引号。
通过命令行-ne
调用perl的一个复杂之处在于,shell会首先蚕食单行代码。这常常导致逃避现实的折磨。
我一直使用的一个"隐藏"功能是" \ x27"包含一个单引号,而不是尝试使用外壳转义"'"`
所以:
$ ls | perl -pe 's/(.*)/stat \x27\x27/' | sh
可以更安全地编写:
$ ls | perl -pe 's/\n/my %seen; for (<LINE>) { print $_ unless $seen{$_}++; }/' | xargs -0 stat
不能使用文件名中的有趣字符,即使这样引用也是如此。但这将:
> 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
下次我们在怪胎聚会上时,将这条衬里放到bash壳中,妇女将蜂拥而至,朋友将崇拜我们:
找 。 -name" * .txt" | xargs perl -pi -e's / 1:(\ S +)/ uc($ 1)/ ge'
处理所有* .txt文件,并使用perl的正则表达式进行就地查找和替换。该代码将'1:'之后的文本转换为大写字母,并删除'1:'。使用Perl的'e'修饰符将查找/替换正则表达式的第二部分视为可执行代码。即时一线模板系统。使用xargs可以处理大量文件,而不会遇到bash的命令行长度限制。
在循环中使用哈希作为可见过滤器的能力。我还没有用其他语言看到过同样不错的东西。例如,我无法在python中复制它。
例如,如果以前没有看到过,我想打印一行。
print my $foo = "foo @{[scalar(localtime)]} bar";
命令行上的新-E选项:
foo Wed May 26 15:50:30 2010 bar
例如,我们可以将函数调用扩展为字符串。
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";
sub _now { my ($now) = localtime() =~ /([:\d]{8})/; return $now; } print _now(), "\n"; # 15:10:33
我们可以在HEREDOCS上使用不同的引号来获得不同的行为。
##代码##@Schwern提到通过本地化$ SIG {__ WARN__}将警告变成错误。我们也可以使用"使用警告FATAL =>" all";"(按词法)执行此操作。参见perldoc lexwarn
。
关于这一点,从Perl 5.12开始,我们已经能够说出perldoc foo
而不是完整的perldoc perlfoo
了。最后! :)
"现在"
##代码##