143

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

指导方针:

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

在其他语言的隐藏功能中也可以找到隐藏功能:

(这些都来自Corion的回答

  • C
    • 达夫的装置
    • 便携性和标准性
  • C#
    • 空格分隔列表和字符串的引号
    • 别名命名空间
  • 爪哇
    • 静态初始化器
  • JavaScript
    • 函数是一等公民
    • 块范围和闭包
    • 通过变量间接调用方法和访问器
  • 红宝石
    • 通过代码定义方法
  • PHP
    • 无处不在的在线文档
    • 魔术方法
    • 符号引用
  • Python
    • 一行值交换
    • 甚至可以用您自己的功能替换核心功能

其他隐藏功能:

运营商:

引用构造:

语法和名称:

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

变量:

循环和流量控制:

常用表达:

其它功能:

其他技巧和元答案:


也可以看看:

4

78 回答 78

54

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

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

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

于 2008-10-02T12:41:44.920 回答
47

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)

其他有趣的东西:use overload你可以重载字符串文字和数字(例如使它们成为 BigInts 或其他)。

许多这些东西实际上都记录在某个地方,或者从记录的功能中合乎逻辑地遵循,但仍有一些不是很为人所知。

更新:另一个不错的。下面提到了q{...}引用结构,但是您知道可以使用字母作为分隔符吗?

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

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

m xabcx
# same as m/abc/
于 2008-10-02T12:50:58.907 回答
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 元字符的文件名所必需的 $_ 引号)

现在该<>功能将解压缩@ARGV以“.gz”或“.Z”结尾的所有文件:

while (<>) {
    print;
}
于 2008-10-02T17:23:05.117 回答
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$c0

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

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

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

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

干杯,

保罗

于 2008-10-02T13:23:29.577 回答
39

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

my $_ = "a"
print -$_

打印-a

print ++$_

打印b

$_ = 'z'
print ++$_

打印aa

于 2008-10-02T12:26:31.867 回答
36

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

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

以防万一您不清楚 Perl 提供了哪些功能,这里有一个可能不完全明显的条目的选择性列表:

Duff 的设备-在 Perl 中

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

文件/路径操作类- File::Find 在比 .Net 更多的操作系统上工作

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

Aliasable 命名空间- 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
于 2008-10-02T13:27:11.487 回答
35

自生化。AFAIK没有其他语言有它

于 2008-10-02T13:48:56.880 回答
31

It's simple to quote almost any kind of strange string in Perl.

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

In fact, the various quoting mechanisms in Perl are quite interesting. The Perl regex-like quoting mechanisms allow you to quote anything, specifying the delimiters. You can use almost any special character like #, /, or open/close characters like (), [], or {}. Examples:

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

Quoting mechanisms:

q : literal quote; only character that needs to be escaped is the end character. qq : an interpreted quote; processes variables and escape characters. Great for strings that you need to quote:

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

qx : Works like qq, but then executes it as a system command, non interactively. Returns all the text generated from the standard out. (Redirection, if supported in the OS, also comes out) Also done with back quotes (the ` character).

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

qr : Interprets like qq, but then compiles it as a regular expression. Works with the various options on the regex as well. You can now pass the regex around as a variable:

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 : A very, very useful quote operator. Turns a quoted set of whitespace separated words into a list. Great for filling in data in a unit test.


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

They're great to use them whenever it makes things clearer. For qx, qq, and q, I most likely use the {} operators. The most common habit of people using qw is usually the () operator, but sometimes you also see qw//.

于 2008-10-02T16:43:04.557 回答
27

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

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

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

注意:上面的不间断空格 (&‎nbsp;) 在其中隐藏了 Unicode 以规避 Markdown。不要复制粘贴它:)

于 2008-10-02T17:11:11.650 回答
27

没有真正隐藏,但是每天都有许多 Perl 程序员不知道CPAN。这尤其适用于不是全职程序员或不全职使用 Perl 编程的人。

于 2008-10-02T17:25:18.247 回答
26

quoteword 运算符是我最喜欢的东西之一。相比:

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 最被低估的特性之一。

于 2008-10-02T16:54:51.003 回答
26

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

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

__DATA__
some good data
some bad data
more good data 
more good data 
于 2008-10-02T17:57:38.670 回答
24

二进制“x”是重复运算符

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

它也适用于列表:

print for (1, 4, 9) x 3; # print 149149149
于 2008-10-02T12:45:51.430 回答
24

污点检查。-t启用污点检查后,如果您尝试将受污染的数据(粗略地说,来自程序外部的数据)传递给不安全的函数(打开文件、运行外部命令等),perl 将死机(或警告)。在编写 setuid 脚本或 CGI 或脚本比提供数据的人具有更大权限的任何内容时,它非常有用。

魔术转到。 goto &sub进行优化的尾调用。

调试器。

use strictuse warnings。这些可以使您免于一大堆拼写错误。

于 2008-10-02T17:00:49.243 回答
24

新区块操作

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

  1. 你声明一个 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 );
        }
    }
    
  2. 然后你可以像这样在体内调用它

    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

您可以在包含您的模块的包中检查某种类型的文字。例如,如果你在你的importsub 中使用它:

overload::constant 
    integer => sub { 
        my $lit = shift;
        return $lit > 2_000_000_000 ? Math::BigInt->new( $lit ) : $lit 
    };

这意味着调用包中每个大于 20 亿的整数都将被更改为一个Math::BigInt对象。(参见重载::constant)。

分组整数文字

当我们在做的时候。Perl 允许您将大数分成三位数的组,并且仍然可以从中得到一个可解析的整数。请注意2_000_000_000上面的 20 亿。

于 2008-10-02T14:31:18.243 回答
22

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

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

在内部转换为此代码:

LINE: while (defined($_ = <ARGV>)) {
    print $_; }{ print "$. Files";
}
于 2008-10-02T12:48:32.377 回答
18

让我们从Spaceship Operator开始。

$a = 5 <=> 7;  # $a is set to -1
$a = 7 <=> 5;  # $a is set to 1
$a = 6 <=> 6;  # $a is set to 0
于 2008-10-02T12:09:33.030 回答
18

这是一个元答案,但Perl Tips档案包含各种可以用 Perl 完成的有趣技巧。以前的提示存档可在线浏览,可通过邮件列表或 atom 订阅源订阅。

我最喜欢的一些技巧包括使用 PAR 构建可执行文件使用 autodie 自动抛出异常,以及在 Perl 5.10 中使用switchsmart-match结构。

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

于 2008-10-02T13:30:52.413 回答
18

map - 不仅因为它使一个人的代码更具表现力,而且因为它让我有一种冲动去阅读更多关于这个“函数式编程”的内容。

于 2008-10-03T15:04:33.620 回答
15

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

于 2008-10-02T12:19:21.633 回答
15

循环的 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";
}
于 2008-10-04T02:29:35.123 回答
13
while(/\G(\b\w*\b)/g) {
     print "$1\n";
}

\G 锚。很热。_

于 2008-10-02T14:25:00.380 回答
13

m//运算符有一些晦涩的特殊情况:

  • 如果您?用作分隔符,除非您调用,否则它只会匹配一次reset
  • 如果您将'其用作分隔符,则不会对模式进行插值。
  • 如果模式为空,则使用上次成功匹配的模式。
于 2008-10-02T13:25:59.167 回答
12

空文件句柄菱形运算符 <>在构建命令行工具中占有一席之地。它的作用就像<FH>从句柄中读取,除了它神奇地选择首先找到的那个:命令行文件名或 STDIN。取自 perlop:

while (<>) {
...         # code for each line
}
于 2008-10-02T13:06:18.650 回答
11

特殊代码块BEGIN,例如CHECKEND。它们来自 Awk,但在 Perl 中的工作方式不同,因为它不是基于记录的。

BEGIN块可用于为解析阶段指定一些代码;它也会在您执行 syntax-and-variable-check 时执行perl -c。例如,要加载配置变量:

BEGIN {
    eval {
        require 'config.local.pl';
    };
    if ($@) {
        require 'config.default.pl';
    }
}
于 2008-10-02T13:16:24.493 回答
11
rename("$_.part", $_) for "data.txt";

将 data.txt.part 重命名为 data.txt 而不必重复自己。

于 2008-10-02T17:12:40.680 回答
10

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

print ~~ localtime;

是相同的

print scalar localtime;

并且不同于

print localtime;
于 2008-10-02T12:42:14.277 回答
9

山羊操作员*

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

或者

sub foo {
    return @_;
}

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

它之所以有效,是因为标量上下文中的列表分配会产生分配的列表中的元素数量。

*注意,不是真正的操作员

于 2009-05-31T03:13:52.790 回答
9

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";

这就是我能想到的尚未提及的事情。

于 2008-10-15T15:13:09.137 回答
9

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

$/ = \3; print $_,"\n" while <>; # output three chars on each line
于 2009-06-22T12:39:06.350 回答
9

tie,变量绑定接口。

于 2008-10-03T21:15:27.940 回答
7

我不知道它有多深奥,但我最喜欢的之一是hash slice。我用它来做各种各样的事情。例如合并两个哈希:

我的 %number_for = (一 => 1, 二 => 2, 三 => 3);
我的 %your_numbers = (二 => 2, 四 => 4, 六 => 6);
@number_for{keys %your_numbers} = 值 %your_numbers;
打印排序值 %number_for; #12346
于 2008-10-03T21:09:09.013 回答
7

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

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

它的工作原理是告诉 nroff 忽略所有代码,然后将手册页的内容放在告诉 Perl 停止处理代码的END标记之后。看起来像这样:

#!/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;

这意味着 that'di';不会产生警告,但'die'; 'did you get that thing I sentcha?';or也不会'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确实警告!

于 2009-02-09T23:00:07.557 回答
7

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

$a = 3;
$b = 4;

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

印刷:3 * 4 = 12

于 2009-05-31T02:39:22.660 回答
6
use diagnostics;

如果您开始使用 Perl 并且以前从未这样做过,那么此模块将为您节省大量时间和麻烦。对于您可以获得的几乎所有基本错误消息,此模块将为您提供有关代码破坏原因的冗长解释,包括一些有关如何修复它的有用提示。例如:

use strict;
use diagnostics;

$var = "foo";

给你这个有用的信息:

全局符号“$var”在第 4 行需要明确的包名。
执行 - 由于编译错误而中止 (#1)
    (F) 你说过“use strict vars”,表示所有变量
    必须是词法范围(使用“my”),事先声明使用
    “我们的”,或明确限定说明全局变量是哪个包
    在(使用“::”)。

用户代码中未捕获的异常:
        全局符号“$var”在第 4 行需要明确的包名。
由于编译错误,执行 - 中止。
 在 - 第 5 行
use diagnostics;
use strict;

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

你会得到这么一大段有用的文本:

语法错误 - 第 5 行,“};”附近
执行 - 由于编译错误而中止 (#1)
(F) 可能意味着您有语法错误。常见原因包括:

    关键字拼写错误。
    缺少一个分号。
    缺少一个逗号。
    缺少左括号或右括号。
    缺少左大括号或右大括号。
    缺少结束引号。

通常会有另一个与语法相关的错误消息
错误提供更多信息。(有时打开 -w 会有所帮助。)
错误消息本身通常会告诉您它在行中的位置
它决定放弃。有时实际错误是几个令牌
在此之前,因为 Perl 擅长理解随机输入。
偶尔行号可能会产生误导,而且千载难逢
找出触发错误的唯一方法是调用
perl -c 反复,每次砍掉一半程序看
如果错误消失了。有点像 S 的控制论版本。

用户代码中未捕获的异常:
    语法错误 - 第 5 行,“};”附近
由于编译错误,执行 - 中止。
在 - 第 7 行

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

于 2009-03-26T17:13:49.820 回答
6
sub load_file
{
    local(@ARGV, $/) = shift;
    <>;
}

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

sub load_file
{
    local @ARGV = shift;
    local $/ = wantarray? $/: undef;
    <>;
}
于 2008-10-02T17:15:17.523 回答
5

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

列表构造函数 1..99 或 'a'..'zz' 也很不错。

于 2008-10-15T17:22:31.870 回答
5

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

$[=1;

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

于 2008-10-02T12:21:46.230 回答
5

@Schwern 提到通过本地化将警告变成错误$SIG{__WARN__}。您也可以使用use warnings FATAL => "all";. 见perldoc lexwarn

关于这一点,自 Perl 5.12 以来,您已经可以说perldoc foo而不是完整的perldoc perlfoo. 最后!:)

于 2010-08-19T23:05:10.487 回答
4

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 次。

于 2009-08-13T06:33:44.747 回答
4

使用左值使您的代码非常混乱:

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

# Then later

bar = 5 ;
print bar ;
于 2008-11-19T15:58:50.260 回答
4

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

use IO::Handle;    
$log->autoflush(1);
于 2008-10-02T19:53:54.227 回答
4

有条件地将字符串或列表添加到其他列表中的一种有用的复合运算符是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();
于 2009-10-31T08:49:34.737 回答
4

使用能力怎么样

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

从一个数组生成一个 hashref 数组—— hashref 前面的 + 消除了块的歧义,因此解释器知道它是一个 hashref 而不是代码块。惊人的。

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

于 2008-10-03T16:43:33.107 回答
4

安全隔间。

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

此致,

于 2008-10-02T18:03:06.753 回答
4

好的。这是另一个。动态范围。在另一篇文章中对此进行了一些讨论,但我没有在隐藏功能中看到它。

像 Autovivification 这样的动态作用域使用它的语言数量非常有限。Perl 和 Common Lisp 是我所知道的仅有的两个使用动态作用域的。

于 2008-10-05T15:12:40.870 回答
4

这个单行代码说明了如何使用 glob 为指定长度的单词 (4) 生成字母表(A、T、C 和 G -> DNA)的所有单词组合:

perl -MData::Dumper -e '@CONV = glob( "{A,T,C,G}" x 4 ); print Dumper( \@CONV )'
于 2011-06-05T16:43:19.027 回答
3

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

$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!';
于 2008-10-28T12:48:30.290 回答
3

您可以在 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";
于 2010-05-26T14:10:53.300 回答
3

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

while (<>) {
  print "$ARGV:$.\t$_";
} 
continue {
  close ARGV if eof
}
于 2008-10-10T02:11:14.207 回答
3

我个人喜欢 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
于 2009-02-09T23:28:12.107 回答
3

Quantum::Superpositions

use Quantum::Superpositions;

if ($x == any($a, $b, $c)) { ...  }
于 2009-07-09T18:29:22.713 回答
3

晚会很晚,但是:属性。

属性本质上允许您定义与变量或子例程的声明相关联的任意代码。使用这些的最好方法是使用Attribute::Handlers;这使得定义属性变得容易(就属性而言,还有什么!)。

我在 YAPC::2006 在线演示了使用它们以声明方式组装可插入类及其插件。这是一个非常独特的功能。

于 2008-11-21T20:27:52.090 回答
3

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

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

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

于 2010-04-13T10:33:13.140 回答
3

use re debug
关于使用重新调试的文档

perl -MO=Concise[,OPTIONS]
简明文档

除了非常灵活、富有表现力和适合以 C、Pascal、Python 和其他语言的风格进行编程之外,还有几个 pragma 命令开关使 Perl 成为我对算法、正则表达式或快速问题进行初始 kanoodling 的“goto”语言需要解决。我相信这两个是 Perl 独有的,也是我的最爱。

use re debug: 大多数现代风格的正则表达式的当前形式和功能都归功于 Perl。虽然有许多 Perl 形式的正则表达式无法用其他语言表达,但几乎没有其他语言的正则表达式形式无法用 Perl 表达。此外,Perl 内置了一个出色的正则表达式调试器,以显示正则表达式引擎如何解释您的正则表达式并匹配目标字符串。

示例:我最近试图编写一个简单的 CSV 例程。(是的,是的,我知道,我应该一直在使用Text::CSV...)但是 CSV 值没有被引用和简单。

我的第一个想法是/^(^(?:(.*?),){$i}/在 n CSV 记录上提取 i 记录。这很好——除了最后一条记录或 n 中的 n。我可以在没有调试器的情况下看到这一点。

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

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

perl -MO=Concise[,OPTIONS]:B 和 Concise 框架是查看 Perl 如何解释您的杰作的绝佳工具。使用-MO=Concise打印源代码的 Perl 解释器翻译的结果。Concise 有很多选项,在 B 中,您可以编写自己的 OP 代码演示文稿。

正如在这篇文章中一样,您可以使用 Concise 来比较不同的代码结构。您可以将源代码行与这些行生成的 OP 代码交错。一探究竟。

于 2010-04-16T20:08:08.943 回答
2

以下与“~~”一样短但更有意义,因为它们指示返回的内容,并且不会与智能匹配运算符混淆:

print "".localtime;   # Request a string

print 0+@array;       # Request a number
于 2009-06-29T17:15:51.400 回答
2

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

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

foo 2010 年 5 月 26 日星期三 15:50:30 酒吧

于 2010-05-26T13:50:09.310 回答
2

我最喜欢的功能是语句修饰符。

不知道我想做多少次:

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

'etc' 让我想起了 5.12 的另一个功能,Yada Yada 运算符。

这很棒,对于您只想要一个占位符的时代。

sub something_really_important_to_implement_later {
    ...
} 

看看:关于 Yada Yada Operator 的 Perl 文档

于 2011-04-12T03:33:03.423 回答
2

Axeman 让我想起了包装一些内置函数是多么容易。

在 Perl 5.10 之前,Perl 没有像 Python 那样漂亮的打印(比如)。

因此,在您的本地程序中,您可以执行以下操作:

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

或添加一些调试。

sub print {
    exists $ENV{DEVELOPER} ?
    print Dumper(@_) :
    print @_;
}
于 2008-10-02T15:04:15.637 回答
2

有两件事可以很好地协同工作: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";
于 2010-05-26T08:44:19.653 回答
2

命令行上的新 -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
于 2010-05-26T11:28:35.297 回答
2

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

例如,如果之前没有见过,我想打印一行。

my %seen;

for (<LINE>) {
  print $_ unless $seen{$_}++;
}
于 2010-05-26T10:35:01.773 回答
1

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

@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显示了这一点。 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});
于 2009-02-13T18:57:47.223 回答
1

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

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

可以改为写

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

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

于 2009-11-05T16:21:50.237 回答
1

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

于 2009-10-10T18:37:15.843 回答
1

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

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

于 2010-01-25T22:51:46.463 回答
1

我参加聚会有点晚了,但投票支持内置的捆绑哈希函数dbmopen()——它对我帮助很大。它不完全是一个数据库,但如果您需要将数据保存到磁盘,它可以解决很多问题并且可以正常工作。当我没有数据库、不了解 Storable.pm 时,它帮助我入门,但我知道我想超越阅读和写入文本文件。

于 2008-10-11T23:07:37.183 回答
1

下次你参加一个极客派对时,把这个装在 bash 壳里的衬里拉出来,女人会蜂拥而至,你的朋友会崇拜你:

寻找 。-name "*.txt"|xargs perl -pi -e 's/1:(\S+)/uc($1)/ge'

处理所有 *.txt 文件并使用 perl 的正则表达式进行就地查找和替换。这个将'1:'之后的文本转换为大写并删除'1:'。使用 Perl 的“e”修饰符将查找/替换正则表达式的第二部分视为可执行代码。即时的单行模板系统。使用 xargs 可以让您处理大量文件,而不会遇到 bash 的命令行长度限制。

于 2010-05-26T10:01:12.637 回答
1

使用哈希(其中键是唯一的)来获取列表的唯一元素:

my %unique = map { $_ => 1 } @list;
my @unique = keys %unique;
于 2009-11-19T12:21:56.950 回答
0

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

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

for $i(1..100) {
    print "Progress $i %\r"
}
于 2008-10-03T10:25:16.017 回答
0

B::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

一个更有用的示例是使用 deparse 找出 coderef 背后的代码,您可能从另一个模块收到这些代码,或者

use B::Deparse;
my $deparse = B::Deparse->new;
$code = $deparse->coderef2text($coderef);
print $code;
于 2012-01-12T21:06:48.403 回答
0

@Corion - Perl 中的裸 URL?当然,即使在插值字符串中也可以。唯一重要的是在您实际用作正则表达式的字符串中。

于 2008-10-02T19:48:08.403 回答
0

$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__"成语类似的功能。

于 2009-09-04T17:30:11.137 回答
0

多一个...

Perl 缓存:

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

关于 Elpeleg 开源,Perl CMS http://www.web-app.net/

于 2009-01-26T07:36:20.793 回答
0

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

例如,让我们使用简单的替换 for ls | xargs stat,天真地完成如下:

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

当输入(文件名)包含空格或 shell 特殊字符(如 |$\. 因此在 Perl 输出中经常需要单引号。

通过命令行调用 perl 的一个复杂之-ne处在于,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
于 2010-05-26T09:54:13.967 回答
0

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

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

sub get_printer {
    my $self = shift;
    {$self->can('print') or $self = $self->next and redo}
}
于 2010-01-14T06:50:33.017 回答
0

“现在”

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

print _now(), "\n"; #  15:10:33
于 2010-09-06T17:25:31.913 回答
-1

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

=> 将 $x 插入到数组 @a 中的 $i 位置

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

@a = ( @a[0..$i-1], $x, @a[$i..$#a] );
于 2011-07-09T00:54:57.290 回答