Perl中有哪些非常有用但却深奥的语言功能,你实际上可以用来做有用的工作?
指南:
尝试限制Perl核心的答案,而不是CPAN
请举例和简短说明
(这些都来自Corion的回答)
C
达夫的装置
便携性和标准性
C#
用于以空格分隔的列表和字符串的引号
可替换的命名空间
Java的
静态初始化剂
JavaScript的
职能是一等公民
阻止范围和关闭
通过变量间接调用方法和访问器
红宝石
通过代码定义方法
PHP
普遍的在线文档
魔术方法
符号引用
蟒蛇
一行值交换
能够使用您自己的功能替换核心功能
运营商:
布尔准算子
触发器操作员
也用于列表构建
在++
和一元-
运营商处理字符串
重复运算符
宇宙飞船运营商
|| operator(和//运算符)从一组选项中进行选择
钻石运营商
m//
运营商的特殊情况
代字号"操作员"
引用结构:
qw运算符
字母可以用作q {}中的引号分隔符 - 类似构造
报价机制
语法和名称:
在sigil之后可以有一个空间
您可以使用符号引用指定子数字名称
法律尾随逗号
分组整数文字
哈希切片
从数组填充哈希的键
模块,Pragma和命令行选项:
使用严格并使用警告
污点检查
深奥使用-n和-p
CPAN
overload::constant
IO :: Handle模块
安全隔间
属性
变量:
自动激活
该$[
变量
领带
动态范围
使用单个语句进行变量交换
循环和流量控制:
魔术转到
for
在一个变量上
继续条款
绝望模式
常用表达:
该\G
锚
(?{})
和'(?? {})`在正则表达式中
其他特性:
调试器
特殊代码块,如BEGIN,CHECK和END
该DATA
块
新的块操作
源过滤器
信号挂钩
地图(两次)
包装内置函数
该eof
功能
该dbmopen
功能
将警告变为错误
其他技巧和元答案:
cat文件,如果需要,解压缩gzips
Perl技巧
也可以看看:
C的隐藏功能
C#的隐藏功能
C++的隐藏功能
Java的隐藏功能
JavaScript的隐藏功能
Ruby的隐藏功能
PHP的隐藏功能
Python的隐藏功能
Clojure的隐藏功能
John Siracus.. 54
触发器操作符用于在循环文件句柄返回的记录(通常是行)时跳过第一次迭代,而不使用标志变量:
while(<$fh>) { next if 1..1; # skip first record ... }
运行perldoc perlop
并搜索"触发器"以获取更多信息和示例.
触发器操作符用于在循环文件句柄返回的记录(通常是行)时跳过第一次迭代,而不使用标志变量:
while(<$fh>) { next if 1..1; # skip first record ... }
运行perldoc perlop
并搜索"触发器"以获取更多信息和示例.
Perl中有许多非显而易见的功能.
例如,你知道sigil之后可以有一个空格吗?
$ perl -wle 'my $x = 3; print $ x' 3
或者,如果使用符号引用,您可以给出子数字名称?
$ perl -lwe '*4 = sub { print "yes" }; 4->()' yes
还有"bool"准运算符,它返回1表示真实表达式,空字符串表示false:
$ 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/
通过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元字符的文件名所需的$ _左右的引号)
现在,该<>
功能将解压缩@ARGV
以".gz"或".Z"结尾的所有文件:
while (<>) { print; }
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.
Cheerio,
保罗
运算符++和一元 - 不仅可以处理数字,还可以处理字符串.
my $_ = "a" print -$_
打印-a
print ++$_
打印b
$_ = 'z' print ++$_
打印aa
由于Perl几乎所有的"深奥"部分来自其他列表,我会告诉你Perl不能做的一件事:
Perl不能做的一件事就是在代码中有任意URL,因为//
运算符用于正则表达式.
为了防止Perl提供的功能不明显,这里有一些可能不是很明显的选项列表:
Duff的设备 - 在Perl中
可移植性和标准性 - 使用Perl的计算机可能多于使用C编译器的计算机
文件/路径操作类 - File :: Find可以在比.Net更多的操作系统上运行
用于以空格分隔的列表 和字符串的引号 - Perl允许您为列表和字符串分隔符选择几乎任意的引号
可替换的命名空间 - Perl通过glob赋值:
*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
自动化.AFAIK 没有其他语言.
在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 //.
"for"语句的使用方法与Pascal中使用的"with"相同:
for ($item) { s/&?nbsp;/ /g; s/<.*?>/ /g; $_ = join(" ", split(" ", $_)); }
您可以将一系列s ///操作等应用于同一变量,而无需重复变量名称.
注意:上面的非中断空格( )中隐藏了Unicode以规避Markdown.不要复制贴吧:)
并非真正隐藏,但Perl程序员每天都不了解CPAN.这尤其适用于不是全职程序员或不在Perl全职编程的人.
引号运算符是我最喜欢的东西之一.相比:
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评价最低的功能之一.
解析直接粘贴到DATA块的数据的能力.无需保存到要在程序中打开的测试文件或类似文件.例如:
my @lines = ; for (@lines) { print if /bad/; } __DATA__ some good data some bad data more good data more good data
二进制"x"是重复运算符:
print '-' x 80; # print row of dashes
它也适用于列表:
print for (1, 4, 9) x 3; # print 149149149
我想说扩展语言的能力,创建伪块操作就是其中之一.
您声明了一个sub的原型,表明它首先需要一个代码引用:
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
sub中使用它:
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亿.
污点检查.启用污点检查后,-t
如果您尝试将污染数据(粗略地说,来自程序外部的数据)传递给不安全的功能(打开文件,运行外部命令等),perl将会死亡(或发出警告).在编写setuid脚本或CGI或脚本具有比提供数据的人更多特权的任何内容时,它非常有用.
魔术转到. goto &sub
做一个优化的尾调用.
调试器.
use strict
和use warnings
.这些可以帮助您摆脱一堆拼写错误.
基于Perl 5中的"-n"
和"-p"
交换机的实现方式,您可以编写一个看似不正确的程序,包括}{
:
ls |perl -lne 'print $_; }{ print "$. Files"'
在内部转换为此代码:
LINE: while (defined($_ =)) { print $_; }{ print "$. Files"; }
让我们从宇宙飞船运营商那里开始.
$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 Tips档案包含可以使用Perl完成的各种有趣的技巧.以前的提示存档是在线浏览,可以通过邮件列表或原子订阅订阅.
我最喜欢的一些技巧包括使用PAR构建可执行文件,使用autodie自动抛出异常,以及在Perl 5.10中使用switch和smart-match构造.
披露:我是Perl Tips的作者和维护者之一,所以我显然非常重视它们.;)
map - 不仅因为它使得一个人的代码更具表现力,而且因为它让我有一种冲动来阅读更多关于这种"函数式编程"的信息.
我的投票将用于Perl正则表达式中的(?{})和(?? {})组.第一个执行Perl代码,忽略返回值,第二个执行代码,使用返回值作为正则表达式.
循环上的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"; }
该m//
运营商有一些模糊的特殊情况:
如果您使用?
分隔符,除非您致电,否则它只匹配一次reset
.
如果'
用作分隔符,则不插入模式.
如果模式为空,则使用上次成功匹配的模式.
while(/\G(\b\w*\b)/g) { print "$1\n"; }
\ G锚.它是热的.
null filehandle 菱形运算符 <>
在构建命令行工具中占有一席之地.它的作用就像
是从句柄中读取,除了它神奇地选择最先找到的那个:命令行文件名或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'; } }
rename("$_.part", $_) for "data.txt";
将data.txt.part重命名为data.txt而不必重复自己.
有点模糊的是波形符号"操作符",它强制标量上下文.
print ~~ localtime;
是相同的
print scalar localtime;
与...不同
print localtime;
tie,变量绑定接口.
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";
这就是我能想到的没有被提及的头脑.
山羊运营商*
:
$_ = "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
我不知道它有多深奥,但我最喜欢的是哈希切片.我把它用于各种各样的事情.例如,合并两个哈希:
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
这个并不是特别有用,但它非常深奥.我在Perl解析器中挖掘时偶然发现了这一点.
在有POD之前,perl4有一个技巧允许你将手册页(如nroff)直接嵌入你的程序中,这样它就不会丢失.perl4使用了一个名为wrapman的程序(请参阅Pink Camel第319页了解一些细节),巧妙地将nroff手册页嵌入到脚本中.
它的工作原理是告诉nroff忽略所有代码,然后在END标记之后放置man页面的内容,告诉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';
.
此外,还有用于数字常量例外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
警告!
您可以使用@ {[...]}来获得复杂perl表达式的插值结果
$a = 3; $b = 4; print "$a * $b = @{[$a * $b]}";
打印: 3 * 4 = 12
sub load_file { local(@ARGV, $/) = shift; <>; }
以及适当返回数组的版本:
sub load_file { local @ARGV = shift; local $/ = wantarray? $/: undef; <>; }
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的人来说,它可以作为一个很好的学习辅助工具.
还有$ [变量决定数组的起始索引.默认值为0,因此数组从0开始.通过设置
$[=1;
如果你真的想要,你可以让Perl表现得更像AWK(或Fortran).
($ x,$ y)=($ y,$ x)是我想要学习Perl的原因.
列表构造函数1..99或'a'..'zz'也非常好.
@Schwern提到通过本地化将警告转化为错误$SIG{__WARN__}
.你也可以这样做(词法)use warnings FATAL => "all";
.见perldoc lexwarn
.
在那个音符上,自从Perl 5.12以来,你已经能够说perldoc foo
而不是完整了perldoc perlfoo
.最后!:)