Perl的隐藏特性?

Hidden features of Perl?

Perl中有哪些真正有用但又深奥的语言特性是您可以用来做有用工作的?

指南:

  • 尝试限制对Perl核心的回答,而不是对CPAN的回答。
  • 请举例说明

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

(这些都是科里奥的答案)

  • C
    • Duff装置
    • 可移植性和标准性
  • C.*
    • 以空格分隔的列表和字符串的引号
    • 可别名命名空间
  • 爪哇
    • 静态初始化器
  • JavaScript
    • 职能是一流的公民
    • 块范围和闭合
    • 通过变量间接调用方法和访问器
  • 红宝石
    • 通过代码定义方法
  • PHP
    • 广泛的在线文档
    • 魔术方法
    • 符号引用
  • Python
    • 单线值交换
    • 能够用自己的功能替换甚至核心功能

其他隐藏功能:

运营商:

  • 布尔拟算子
  • 触发器运算符
    • 也用于列表构造
  • ++和一元-运算符处理字符串。
  • 重复运算符
  • 宇宙飞船操作员
  • 要从一组选项中选择的运算符(和//运算符)
  • 钻石经营者
  • m//算子的特殊情况
  • tilde tilde"运算符"

引用构造:

  • QW算子
  • 在类q构造中,字母可以用作引号分隔符。
  • 报价机制

语法和名称:

  • 一个信号后会有一个空格
  • 可以用符号引用给出子数字名称
  • 合法尾随逗号
  • 分组整数文本
  • 哈希切片
  • 从数组填充哈希的键

模块、pragma和命令行选项:

  • 严格使用和使用警告
  • 污点检查
  • 深奥地使用-n和-p
  • CPAN
  • overload::constant
  • IO::句柄模块
  • 安全隔间
  • 属性

变量:

  • 自活化
  • $[变量
  • 领带
  • 动态范围
  • 变量与单个语句交换

回路和流量控制:

  • 魔术游戏
  • 单变量for
  • 延续子句
  • 绝望模式

正则表达式:

  • \G
  • (?{})和'(??)`在regex中

其他特点:

  • 调试器
  • 特殊代码块,如begin、check和end
  • DATA区块
  • 新块操作
  • 源滤波器
  • 信号钩子
  • 地图(两次)
  • 包装内置函数
  • eof函数
  • dbmopen函数
  • 将警告转换为错误

其他技巧和元答案:

  • cat文件,根据需要解压缩gzip
  • 珀尔小费

参见:

  • C的隐藏特征
  • C的隐藏特征#
  • C++的隐藏特性
  • Java的隐藏特性
  • javascript的隐藏特性
  • 红宝石的隐藏特征
  • PHP的隐藏功能
  • Python的隐藏特征
  • Clojure的隐藏特征


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

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

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


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

例如,你知道在一个信号之后会有一个空格吗?

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

或者,如果使用符号引用,可以给子数字名称?

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

还有"bool"类运算符,它为true表达式返回1,为false返回空字符串:

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

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

这些东西中的许多实际上是在某个地方记录的,或者从记录的特性逻辑上遵循的,但是尽管如此,其中一些并不是很有名。

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

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

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

1
2
m xabcx
# same as m/abc/


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

1
2
3
4
5
6
7
8
9
10
11
12
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元字符的文件名时需要在$u左右加引号)

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

1
2
3
while (<>) {
    print;
}


Perl中我最喜欢的特性之一是使用Boolean ||操作符在一组选项之间进行选择。

1
2
3
4
 $x = $a || $b;

 # $x = $a, if $a is true.
 # $x = $b, otherwise

这意味着你可以写:

1
 $x = $a || $b || $c || 0;

$a$b$c的第一个真值,否则取0的违约值。

在Perl5.10中,还有//操作符,如果定义了它,则返回左侧,否则返回右侧。下面从$a$b$c0中选择第一个定义值,否则:

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

这些还可以与它们的简短表单一起使用,这对于提供默认值非常有用:

1
2
3
$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.

再见,

保罗


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

1
2
my $_ ="a"
print -$_

打印-A

1
print ++$_

印刷B

1
2
$_ = 'z'
print ++$_

印花AA


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

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

为了防止Perl提供的特性对您不明显,这里有一个可能并不完全明显的条目的选择性列表:

达夫的设备-在Perl中

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

文件/路径操作类-file::find比.net在更多操作系统上工作

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

别名名称空间-Perl通过全局分配具有这些名称空间:

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

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

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

块范围和闭包-Perl同时具有

通过变量间接调用方法和访问器-Perl也这样做:

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

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

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

普适在线文档-Perl文档是在线的,也可能在您的系统上。

每次调用"不存在"函数时都会调用的magic方法-perl在autoload函数中实现该方法

符号参考-建议您远离这些。他们会吃你的孩子。当然,Perl允许你把你的孩子献给嗜血的恶魔。

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

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

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

1
2
3
4
5
BEGIN{
    *CORE::GLOBAL::unlink = sub {print 'no'}
};

unlink($_) for @ARGV


自动活体化。阿法克没有其他语言。


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

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

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

1
2
3
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:一个解释过的引号;处理变量和转义字符。非常适合您需要引用的字符串:

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

QX:与QQ类似,但随后以非交互方式作为系统命令执行。返回从标准输出生成的所有文本。(重定向,如果操作系统支持,也会出现)也可以用反引号(字符)完成。

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

qr:像qq一样解释,但随后将其编译为正则表达式。还可以在regex上使用各种选项。现在可以将regex作为变量传递:

1
2
3
4
5
6
7
8
9
10
11
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:一个非常,非常有用的报价运算符。将一组带引号的空格分隔词转换为列表。非常适合在单元测试中填充数据。

1
2
3
4
5
   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/。


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


"for"语句的用法与"with"在pascal中的用法相同:

1
2
3
4
5
6
for ($item)
{
    s/&?nbsp;/ /g;
    s/<.*?>/ /g;
    $_ = join("", split("", $_));
}

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

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


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

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

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

噪音小得多,眼睛容易。Perl的另一个非常好的地方是,在编写SQL时,后面的逗号是合法的:

1
print 1, 2, 3, ;

这看起来很奇怪,但如果您以另一种方式缩进代码,则不会:

1
2
3
4
5
print
    results_of_foo(),
    results_of_xyzzy(),
    results_of_quux(),
    ;

向函数调用中添加其他参数不需要在前面或后面的行中乱加逗号。单线变化对周围线路没有影响。

这使得使用可变函数非常愉快。这可能是Perl最不受重视的特性之一。


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

1
2
3
4
5
6
7
8
9
10
my @lines = <DATA>;
for (@lines) {
    print if /bad/;
}

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


污染检查。启用污染检查后,如果试图将污染数据(大致来说,来自程序外部的数据)传递到不安全的函数(打开文件、运行外部命令等),Perl将死亡(或警告,使用-t)。当编写setuid脚本或cgi或任何脚本比提供它数据的人具有更大权限的情况时,它非常有用。

魔术GTO。goto &sub执行优化的尾调用。

调试器。

use strictuse warnings。这些可以帮你避免打字错误。


新块操作

我想说的是扩展语言、创建伪块操作的能力。

  • 您声明Sub的原型,表示它首先接受代码引用:

    1
    2
    3
    4
    5
    6
    sub do_stuff_with_a_hash (&\%) {
        my ( $block_of_code, $hash_ref ) = @_;
        while ( my ( $k, $v ) = each %$hash_ref ) {
            $block_of_code->( $k, $v );
        }
    }
  • 你可以这样在身体里叫它

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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是另一个半隐藏的gem。)请注意,您不需要在块前面使用sub关键字,也不需要在哈希之前使用逗号。最后看起来很像:map { } @list

    源滤波器

    此外,还有源过滤器。Perl将把代码传递给您以便您操作它。这两种操作,以及块操作,基本上都是"不要尝试"这种"在家"类型的操作。

    我对源过滤器做了一些很好的工作,例如创建一种非常简单的语言来检查时间,允许使用简短的Perl一行程序来做一些决策:

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

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

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

    信号钩子

    做得够多了,但并不那么明显。这是一个靠在旧的模具上的模具管理员。

    1
    2
    3
    4
    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中使用此选项:

    1
    2
    3
    4
    5
    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亿元。


    二进制"x"是重复运算符:

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

    它也适用于列表:

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


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

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

    在内部转换为该代码:

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


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

    我最喜欢的一些技巧包括用par构建可执行文件,使用autodie自动抛出异常,以及使用perl 5.10中的switch和smart-match构造。

    公开:我是Perl技巧的作者和维护者之一,所以我显然非常看重它们。;)


    地图——不仅因为它使代码更具表现力,而且因为它让我有冲动去阅读更多关于这个"函数式编程"的内容。


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

    1
    2
    3
    $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代码,忽略返回值,第二个执行代码,使用返回值作为正则表达式。


    循环上的continue子句。它将在每个循环的底部执行,甚至是下一个循环。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    while( <> ){
      print"top of loop
    "
    ;
      chomp;

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

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

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

    G锚。天气很热。


    m//运算符有一些模糊的特殊情况:

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


    空的filehandle diamond操作符<>在构建命令行工具中占有一席之地。它像一样从句柄中读取,只是它神奇地选择首先找到的内容:命令行文件名或stdin。取自Perlop:

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


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    BEGIN {
        eval {
            require 'config.local.pl';
        };
        if ($@) {
            require 'config.default.pl';
        }
    }


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

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


    有点模糊的是tilde tilde"operator",它强制使用标量上下文。

    是一样的

    不同于


    山羊经营者dOCx1〔14〕:

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

    1
    2
    3
    4
    5
    sub foo {
        return @_;
    }

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

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

    *注,不是真正的操作员


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

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

    Perl的循环控制结构的"绝望模式"导致它们查找堆栈以找到匹配的标签,这使得测试::更多地利用了一些奇怪的行为,不管好坏。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SKIP: {
        skip() if $something;

        print"Never printed";
    }

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

    有一个鲜为人知的.pmc文件。"使用foo"将在foo.pm之前在@inc中查找foo.pmc。这是为了允许首先加载已编译的字节码,但module::compile利用这一点缓存源筛选模块,以加快加载时间并简化调试。

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

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

    这是我能想到的,我的头上没有提到。


    TIE,变量绑定接口。


    我不知道这有多深奥,但我最喜欢的是散列切片。我用它做各种各样的东西。例如,要合并两个哈希:

    1
    2
    3
    4
    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的程序(有关一些详细信息,请参见PinkCamel第319页)将nroff手册页巧妙地嵌入到脚本中。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #!/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上下文中的常量会产生警告。在op.c中有一些特殊的例外,允许以某些roff命令开头的无效上下文字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
                  /* 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;。法典声称这是为了更普遍的目的。

    1
    2
    3
    4
    5
                /* 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;

    你知道吗,以东十一〔八〕警告你!


    可以使用@[…]获取复杂Perl表达式的插值结果

    1
    2
    3
    4
    $a = 3;
    $b = 4;

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

    印刷品:3 * 4 = 12


    1
    2
    3
    4
    5
    sub load_file
    {
        local(@ARGV, $/) = shift;
        <>;
    }

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

    1
    2
    3
    4
    5
    6
    sub load_file
    {
        local @ARGV = shift;
        local $/ = wantarray? $/: undef;
        <>;
    }

    1
    use diagnostics;

    如果您开始使用Perl并且以前从未这样做过,那么这个模块将为您节省大量的时间和麻烦。对于您能得到的几乎每一条基本错误消息,本模块都会给您一个详细的解释,说明您的代码为什么会被破坏,包括一些关于如何修复它的有用提示。例如:

    1
    2
    3
    4
    use strict;
    use diagnostics;

    $var ="foo";

    给你这个有用的信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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
    1
    2
    3
    4
    5
    6
    use diagnostics;
    use strict;

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

    你会得到大量有用的文本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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
    $[=1;

    如果您真的想让Perl的行为更像awk(或fortran)。


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

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


    @Schwern提到通过定位$SIG{__WARN__}将警告变成错误。你也可以用use warnings FATAL =>"all";来做这个(词汇上的)。见perldoc lexwarn

    从Perl5.12开始,您就可以说perldoc foo而不是完整的perldoc perlfoo。终于!:)


    使用lvalues使代码非常混乱:

    1
    2
    3
    4
    5
    6
    7
    my $foo = undef ;
    sub bar:lvalue{ return $foo ;}

    # Then later

    bar = 5 ;
    print bar ;

    这一行说明了如何使用glob为指定长度(4)的单词生成字母表(a、t、c和g->dna)的所有单词组合:

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

    Schwartzian转换是一种技术,它允许您高效地按计算的辅助索引排序。假设您希望按字符串的MD5和对其列表进行排序。下面的评论最好向后读(这是我写这些文章的顺序):

    1
    2
    3
    4
    5
    6
    7
    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次日志。


    安全隔间。

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

    最好的问候,


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

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


    好的。这是另一个。动态范围界定。有人在另一个帖子里说了一点,但我在这里没有看到隐藏的特征。

    像自动激活这样的动态范围界定,使用它的语言数量非常有限。Perl和CommonLisp是我所知道的唯一使用动态范围界定的两个。


    使用能力如何

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

    要从数组生成一个hashref s数组,hashref前面的+将消除块的歧义,这样解释器就知道它是hashref而不是代码块。令人惊叹的。

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


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

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

    此运算符允许使用类似于

    1
     do_something() if test();

    我个人喜欢S///操作的/E修饰符:

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

    输入:

    1
    2
    This is a test of regular expressions
    ^D

    输出(我认为):

    1
    sihT si a tset fo regular expressions

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

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

    除了具有出色的灵活性、表现力和易于编程的C、Pascal、python和其他语言的风格之外,还有几个pragma命令开关,使perl成为我的"goto"语言,用于初始了解需要解决的算法、regex或快速问题。我相信这两个是Perl独有的,也是我最喜欢的。

    use re debug:大多数现代风格的正则表达式的当前形式和功能都归功于Perl。尽管有许多不能用其他语言表达的regex的perl形式,但几乎没有任何其他语言风格的regex形式不能用perl表达。此外,Perl还内置了一个极好的regex调试器,演示regex引擎如何解释regex并与目标字符串进行匹配。

    我最近在写一个简单的csv程序。(是的,是的,我知道,我本应该使用文本::csv…)但是csv值没有被引用并且很简单。

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

    接着我又试了一下/^(?:(.*?),|$){$i}/,但没用,我不知道为什么。我想我是在说"以东十一号"(5),然后是逗号或eol。然后我在一个小测试脚本的顶部添加了use re debug。啊,是的,以东十一〔七〕之间的变化不是这样解释的;它被解释为以东十一〔八〕而不是我想要的。

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

    perl -MO=Concise[,OPTIONS]:B和简明框架是查看Perl如何解释您的杰作的强大工具。使用-MO=Concise打印源代码的Perl解释器翻译的结果。有许多简洁的选项,在B中,您可以编写自己的操作代码表示。

    在本文中,您可以使用简洁来比较不同的代码结构。您可以将源代码行与这些行生成的操作代码交错。过来看。


    Perl最喜欢的半隐藏特性是eof函数。下面是一个直接来自perldoc -f eof的例子,说明如何使用它来重置文件名,以及如何在命令行加载的多个文件之间轻松地使用EDOCX1(当前行号):

    1
    2
    3
    4
    5
    6
    while (<>) {
      print"$ARGV:$.\t$_";
    }
    continue {
      close ARGV if eof
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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
    "
    ;

    您可以将正则表达式和字符串中的分隔符替换为几乎所有其他内容。这对于"倾斜牙签综合征"特别有用,例如:

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

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

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

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

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

    要混淆您的朋友(和语法高亮显示),请尝试以下操作:

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


    参加聚会很晚,但是:性格。

    属性本质上允许您定义与变量或子例程声明关联的任意代码。使用这些属性的最佳方法是使用attribute::handlers;这使得定义属性变得容易(就其他属性而言!).

    我做了一个关于使用它们声明性地组装一个可插拔类及其插件的演示,网址是yapc::2006,在线这里。这是一个非常独特的功能。


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

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

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


    Quantum::Superpositions

    1
    2
    3
    use Quantum::Superpositions;

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

    有两件事可以很好地结合在一起:IO处理核心字符串,并使用函数原型使您能够用grep/map样的语法编写自己的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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,"
    "
    ;

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

    在Perl5.10之前,Perl没有像python那样的漂亮打印。

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

    1
    2
    3
    4
    sub print {
         print @_,"
    "
    ;
    }

    或者添加一些调试。

    1
    2
    3
    4
    5
    sub print {
        exists $ENV{DEVELOPER} ?
        print Dumper(@_) :
        print @_;
    }


    在循环中使用哈希作为可见筛选器的能力。我还没有用另一种语言看到同样美好的事物。例如,我还不能在Python中复制它。

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

    1
    2
    3
    4
    5
    my %seen;

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

    命令行上的新-e选项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    > 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

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

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

    foo Wed May 26 15:50:30 2010 bar


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

    不知道我想做多少次:

    1
    2
    3
    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 Operator。

    这太好了,在你只想拥有一个位置的时候。

    1
    2
    3
    sub something_really_important_to_implement_later {
        ...
    }

    查看:yada yada operator上的perl文档。


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

    1
    2
    3
    print"".localtime;   # Request a string

    print 0+@array;       # Request a number

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

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

    可以改为写

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

    这更详细,但允许从数据文件进行填充。另外,如果出于任何原因在源代码中维护列表,那么维护数组和regexp就更容易了。


    下次你去参加一个极客聚会时,把这一行用巴什壳抽出来,女人们会蜂拥着你,你的朋友们会崇拜你:

    找到。-名称"*.txt"xargs perl-pi-e's/1:(s+)/uc($1)/ge'

    处理所有*.txt文件,并使用Perl的regex进行就地查找和替换。此操作将"1"后的文本转换为大写,并删除"1"。使用Perl的"e"修饰符将find/replace regex的第二部分视为可执行代码。即时单线模板系统。使用xargs,您可以处理大量文件,而不会遇到bash的命令行长度限制。


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

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


    我参加聚会有点晚,但是投票支持内置的绑定哈希函数dbmopen()--这对我有很大帮助。它不完全是一个数据库,但如果您需要将数据保存到磁盘上,它会消除很多问题,并且只会起作用。当我没有数据库,不了解storable.pm时,它帮助我开始了工作,但我知道我想超越对文本文件的读写。


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

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

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


    您可能认为可以这样做来保存内存:

    1
    2
    3
    @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仍然为每个键分配不同的标量值。Peek显示了这一点。PVHV是散列值。Elt是一个键,后面的SV是它的值。注意,每个SV都有一个不同的内存地址,表明它们没有被共享。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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的陷阱。

    1
    2
    3
    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});


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

    1
    2
    3
    4
    5
    6
    $| = 1; # flush the buffer on the next output

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

    }


    Perl是一个灵活的awk/sed。

    例如,让我们用一个简单的替代方法来代替ls | xargs stat,简单地做如下:

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

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

    通过命令行-ne调用perl的一个复杂之处是,shell首先会被您的一行程序咬到。这往往会导致痛苦的逃跑以满足它。

    我一直使用的一个"隐藏"功能是\x27,它包含一个单引号,而不是试图使用shell转义'\''

    所以:

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

    可以更安全地写:

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

    这对文件名中的搞笑字符不起作用,即使是这样引用的。但这将:

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 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__"习惯用法相似的功能。


    再一个。。。

    Perl缓存:

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

    论ELPELEG开源,Perl-CMS网址:http://www.web-app.net/


    B::deparse-生成Perl代码的Perl编译器后端。这不是您在日常Perl编码中使用的东西,但在特殊情况下可能有用。

    如果遇到一些模糊的代码或复杂的表达式,请通过Deparse传递它。有助于找出golfed的japh或perl代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ 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背后的代码,该代码可能是从另一个模块收到的,或者

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


    "现在"

    1
    2
    3
    4
    5
    6
    7
    sub _now {
            my ($now) = localtime() =~ /([:\d]{8})/;
            return $now;
    }

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


    @Corion-BareURL在Perl中?当然可以,即使是在插入字符串中。唯一重要的时间是在实际用作正则表达式的字符串中。


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

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

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

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

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

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

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