Perl 的一些优雅特性或用途是什么?

36

"什么?Perl漂亮?优雅?他一定是在开玩笑!"

事实是,有些Perl代码确实很丑陋。我指的是很多。我们都见过。

"当然了,它就是一个符号汤。不是吗?"

是的,有符号。就像“数学”有“符号”一样。只是我们程序员更熟悉标准的数学符号。我们接受来自母语的符号,无论是ASM、C还是Pascal。Perl只是决定要多一些。

"好吧,我认为我们应该摆脱所有不必要的符号。这样代码看起来更好看。"

已经存在用于这样做的语言。它叫做Lisp。(以及即将到来的perl 6。)

"好的,聪明的家伙。事实是,我已经可以发明自己的符号了。它们被称为函数和方法。此外,我们不想重新发明APL。"

哦,虚假的替身,你太有趣了!事实上,Perl可以非常美丽。它也可以非常丑陋。在Perl中,TIMTOWTDI

那么,你最喜欢的优雅的Perl代码是什么?


嗯... 标签混乱?Perl作为一种编程语言而非标记语言,不太会有标签。如果您指的是'man perlfunc'中的内容,那些是函数。 - chaos
也许这是一个操作符混合物... - Chris Lutz
4
在另一种语言中,这段代码将扩展为20行代码。 - 1800 INFORMATION
5
“非 ASCII 字符与 ASCII 字符之比”,上次我查看时,所有这些字符都是 ASCII 字符:{ } < > = $ _ @ -。 - AmbroseChapel
@ysth,坚持使用TMTOWTDI的规范拼写似乎与TMTOWTDI的精神背道而驰。 :-) (我敢肯定沃尔在一次State of the Onion中曾经提到过这一点,但我找不到了。) - LSpice
显示剩余7条评论
14个回答

29

Perl使用列表/哈希表来实现命名参数,我认为这非常优雅,并且对于自我文档化的代码是一种巨大的帮助。

my $result = $obj->method(
    flux_capacitance       => 23,
    general_state          => 'confusion',
    attitude_flags         => ATTITUDE_PLEASANT | ATTITUDE_HELPFUL,
);

1
我必须说,这也是我最喜欢的功能之一。有趣的事实是,这也将应用于.NET。 :) - Robert P

24

我最喜欢的优雅Perl代码并不一定真的优雅。它们是“元优雅”的,可以帮助你摆脱许多Perl开发者陷入的坏习惯。详细介绍所有这些代码需要花费数小时或数天的时间,但简要列举如下:

  • autobox,将Perl的基本类型转换为一等对象。
  • autodie,使内置函数在失败时抛出异常(消除了大部分需要使用or die...结构的需求)。另请参阅我的autodie博客视频
  • Moose,提供了一种优雅、可扩展且正确的编写Perl类的方式。
  • MooseX::Declare,在使用Moose时提供语法上的便利性。
  • Perl::Critic,您个人的、自动化的、可扩展的、知识渊博的代码审查员。另请参阅此Perl提示
  • Devel::NYTProf,为我提供了我在任何编程语言中看到的最详细和可用的分析信息。另请参阅Tim Bunce的博客
  • PAR,Perl Archiver,用于捆绑分发包甚至将整个程序转换为独立的可执行文件。另请参阅此Perl提示
  • Perl 5.10,提供了一些惊人的regexp改进smart-matchswitch语句defined-or和state变量
  • Padre,唯一集成了以上最佳功能的Perl编辑器,跨平台,完全免费且开源。
如果您懒得点击链接,我最近在Linux.conf.au 发表了一篇关于上述内容的演讲。如果您错过了,可以在网上找到视频(ogg Theora)。如果您懒得观看视频,今年我将在OSCON上作为教程做一个演讲的大幅扩展版本(名为doing Perl right)。
祝一切顺利,
Paul

19

我惊讶地发现没有人提到Schwartzian变换。

my @sorted =
  map  { $_->[0] }
  sort { $a->[1] <=> $b->[1] }
  map  { [ $_, expensive_func($_) ] }
@elements;

如果没有 slurp 运算符,

my $file = do { local $/; readline $fh };

1
那个(S.T.)真是太漂亮了。 - chaos
被 Perl 6 弃用了,因为它已经内置了! - Chris Dolan
2
它只是修复了 Perl sort 没有可以缓存的键函数,只有比较函数的事实。Python 的新 key 参数可以做到这一点。ST 无论如何都很美,但来自其他语言的人会认为 Perl sort 需要这样一个奇怪的咒语来执行如此常见的操作,因此需要修复。 - Mathieu Longtin
3
这实际上被称为“装饰-排序-去装饰”,起源于Lisp。 - Pedro Silva

16

如果用户提供了一组要处理的文件列表,而您又不想意外地处理程序、文件夹或不存在的文件,可以尝试以下方法:

@files = grep { -T } @files;

就像魔术一样,您已经筛选出了所有不适当的条目。不想悄悄地忽略它们吗?在最后一行之前添加以下行:

warn "Not a file: $_" foreach grep { !-T } @files;

将无法处理的每个文件打印出漂亮的警告消息到标准错误流。不使用 grep 的情况下,相同的操作看起来像这样:

my @good;
foreach(@files) {
  if(-T) {
    push @good, $_;
  } else {
    warn "Not a file: $_";
  }
}

grep(和map)可以使代码更短,同时仍保持它非常易读。


关于“警告”的建议让我感到不舒服,因为它需要对列表进行两次遍历。另一方面,虽然很容易将其转换为一次遍历,但它变得更像您不太优雅的清单。我想到的可怕的一次遍历“map”版本是map { warn "Not a file: $_" unless -T, $_ } @files - LSpice

11

“or die”结构:

open my $fh, "<", $filename
    or die "could not open $filename: $!";

使用 qr// 创建语法:

#!/usr/local/ActivePerl-5.10/bin/perl

use strict;
use warnings;
use feature ':5.10';

my $non_zero         = qr{[1-9]};
my $zero             = qr{0};
my $decimal          = qr{[.]};
my $digit            = qr{$non_zero+ | $zero}x;
my $non_zero_natural = qr{$non_zero+ $digit*}x;
my $natural          = qr{$non_zero_natural | $zero}x;
my $integer          = qr{-? $non_zero_natural | $zero}x;
my $real             = qr{$integer (?: $decimal $digit)?}x;

my %number_types = (
    natural => qr/^$natural$/,
    integer => qr/^$integer$/,
    real    => qr/^$real$/
);

for my $n (0, 3.14, -5, 300, "4ever", "-0", "1.2.3") {
    my @types = grep { $n =~ $number_types{$_} } keys %number_types;
    if (@types) {
        say "$n is of type", @types == 1 ? " ": "s ", "@types";
    } else {
        say "$n is not a number";
    }
}

使用匿名子程序来分解重复代码:

my $body = sub {
    #some amount of work
};

$body->();
$body->() while $continue;

而不是

#some amount of work
while ($continue) {
    #some amount of work again
}

基于哈希的调度表:

my %dispatch = (
    foo => \&foo,
    bar => \&bar,
    baz => \&baz
);

while (my $name = iterator()) {
    die "$name not implemented" unless exists $dispatch{$name};
    $dispatch{$name}->();
}

取代

while (my $name = iterator()) {
    if ($name eq "foo") {
        foo();
    } elsif ($name eq "bar") {
        bar();
    } elsif ($name eq "baz") {
        baz();
    } else {
        die "$name not implemented";
    }
}

$body->(); $body->() while $continue(你可能想表达的是 $continue->() …)更好的方法是 do { $body->() } while $continue,它实现了相同的效果,保证 $body->() 将被执行一次 (http://perldoc.perl.org/perlsyn.html#Statement-Modifiers)。 - LSpice

9
我喜欢的一个例子是Perl实现的阶乘计算器。在Perl 5中,它看起来像这样:
use List::Util qw/reduce/;
sub factorial {
    reduce { $a * $b } 1 .. $_[0];
}

如果数字小于等于1或者是字符串,返回false;如果传入的是数字,则返回该数字(如果是小数则向下取整)。

而对于Perl 6的期待,它看起来像是这样:(链接)

sub factorial {
    [*] 1..$^x
}

此外(来自上面链接的博客),您甚至可以将其实现为运算符:

sub postfix:<!>(Int $x) {
    [*] 1..($x || 1)
}

然后您可以像这样在代码中使用它:

my $fact5 = 5!;

我相信有一种稍微长一点的方法来实现阶乘函数,即在数字后面添加"!"以表示数学符号12!,但我不熟悉Perl 6,所以我不记得了。 - Chris Lutz
Perl 6 的三元运算符是 ?? !! 而不是 ? :,没错。 - Robert P
1
使用 ?? 和 !! 让你的 Perl 代码看起来像在“搞什么鬼?”而且这让我觉得很有趣。 - Chris Lutz
@Brad - 看起来很不错,但如果没有指定参数为Int,我们就有可能发现""的阶乘是1,这会让人感到困惑。 - Chris Lutz
不需要导入任何模块,replace 可以被 for 替代:sub { my $fact = 1; $fact *= $_ for 1 .. $_[0] }。这看起来可能不是特别优雅,但在我看来,它似乎是对 reduce 所做的事情相当紧凑的翻译(并且通过某种程度上的蛮力方法给出了 0 的阶乘的正确答案)。 - LSpice
显示剩余4条评论

9

具有构造函数、getter/setter和类型验证的三行类:

{
    package Point;
    use Moose;

    has ['x', 'y'] => (isa => 'Num', is => 'rw');
}

package main;
my $point = Point->new( x => '8', y => '9' );

$point->x(25);

2
Moose 真的让 Perl 成为了理想的面向对象编程平台(在我看来)。 - draegtun
顺便说一句...你也可以这样写...has ['x', 'y'] => (isa => 'Num', is => 'rw'); - draegtun
谢谢draegtun,你的修改让它更性感 ;) - brunov

7
如果您有一个逗号分隔的标志列表,并且想要一个查找表,那么您需要做的就是:
my %lookup = map { $_ => 1 } split /,/, $flags;

现在,您可以简单地测试您需要哪些标志,如下所示:
if ( $lookup{FLAG} ) {
    print "Ayup, got that flag!";
}

地图保证每个标志始终为1。 - Robert P
啊,好的。抱歉,我完全看到那之前有一行代码,但是没有读它。对不起。 - Chris Lutz
一个接受命令行参数的程序的微不足道的修改是将 "split(...)" 更改为 "@ARGV"。这样,您可以使用 %lookup{-e} 访问每个标志的 "-e" 标志。来自命令行的任何文件都将被传递,但我怀疑您的程序是否会检查它们,所以没关系。 - Chris Lutz

6

我很惊讶没有人提到这个。在我看来,这是一件杰作

#!/usr/bin/perl $==$'; $;||$.| $|;$_ ='*$ ( ^@(%_+&~~;# ~~/.~~ ;_);;.);;#) ;~~~~;_,.~~,.* +,./|~ ~;_);@-, .;.); ~ ~,./@@-__);@-);~~,.*+,. /|);;;~~@-~~~~;.~~,. /.);;.,./@~~@-;.;#~~@-;; ;;,.*+,./.);;#;./@,./ |~~~~;#-(@-__@-__&$#%^';$__ ='`'&'&';$___="````" |"$[`$["|'`%",';$~=("$___$__-$[``$__"| "$___"| ("$___$__-$[.%")).("'`"|"'$["|"'#"). '/.*?&([^&]*)&.*/$'.++$=.("/``"|"/$[`"|"/#'").(";`/[\\`\\`$__]//`;" |";$[/[\\$[\\`$__]//`;"|";#/[\\\$\\.$__]//'").'@:=("@-","/.", "~~",";#",";;",";.",",.",");","()","*+","__","-(","/@",".%","/|", ";_");@:{@:}=$%..$#:;'.('`'|"$["|'#')."/(..)(..)/".("```"|"``$["| '#("').'(($:{$'.$=.'}<<'.(++$=+$=).')|($:{$'.$=.'}))/'.("```;"| "``$[;"|"%'#;").("````'$__"|"%$[``"|"%&!,").${$[};`$~$__>&$=`;$_= '*$(^@(%_+&@-__~~;#~~@-;.;;,.(),./.,./|,.-();;#~~@-);;;,.;_~~@-,./., ./@,./@~~@-);;;,.(),.;.~~@-,.,.,.;_,./@,.-();;#~~@-,.;_,./|~~@-,. ,.);););@-@-__~~;#~~@-,.,.,.;_);~~~~@-);;;,.(),.*+);;# ~~@-, ./|,.*+,.,.);;;);*+~~@-,.*+,.;;,.;.,./.~~@-,.,.,.;_) ;~~~ ~@-,.;;,.;.,./@,./.);*+,.;.,.;;@-__~~;#~~@-,.;;,.* +);; #);@-,./@,./.);*+~~@-~~.%~~.%~~@-;;__,. /.);;#@- __@- __ ~~;;);/@;#.%;#/.;#-(@-__~~;;;.;_ ;#.%~~~~ ;;() ,.;.,./@,. /@,.;_~~@- ););,.;_ );~~,./ @,. ;;;./@,./| ~~~~;#-(@- __,.,.,. ;_);~~~ ~@ -~~());; #);@-,./@, .*+);;; ~~@-~~ );~~);~~ *+~~@-);-( ~~@-@-_ _~~@- ~~@-);; #,./@,.;., .;.);@ -~~@-; #/.;#-( ~~@-@-__ ~~@-~~ @-);@ -);~~, .*+,./ |);;;~ ~@-~~ ;;;.; _~~@-@ -__);. %;#-( @-__@ -__~~;# ~~@-;; ;#,. ;_,.. %);@-,./@, .*+, ..%, .;.,./|) ;;;) ;;#~ ~@-,.*+,. ,.~~ @-); *+,.;_);;.~ ~);); ~~,.; .~~@-);~~,.;., ./.,.; ;,.*+

不幸的是,WMD编辑器破坏了代码。还有另一种可行的变体:http://www.perlmonks.org/index.pl?node_id=45213 - spoulson
我同意这是一件杰作,但当我真正理解它时,我会更加欣赏它。 - Chris Lutz
Chris Lutz:这是可能编写的最自我记录的代码。http://search.cpan.org/perldoc?Acme::EyeDrops - singingfish
嗯,这很整洁,但我觉得不太优雅。 - Robert P
我用 < 替换了 <,用 > 替换了 >。希望这样能修复错误。 - Brad Gilbert
@Robert P:它很优美,只是以一种难以置信的扭曲方式呈现。 - singingfish

3

我非常喜欢Black Perl(链接是为了版本适应Perl 5而重写)。它能编译,但据我所知实际上并没有做任何事情。

这就是用一种从实用角度而非理论角度撰写的语言而得到的结果。

除此之外,你可以将人们抱怨的Perl称为pidgin Perl(非常有用,但表达起来不够表现力,当心试图在其中表达复杂的内容),而@pjf所说的内容则是“正式”的Perl,这是莎士比亚、海明威、休谟等人使用的语言。【编辑:呃,虽然比休谟更易于阅读,比莎士比亚不那么过时。】【重新编辑,希望不要像海明威那样醉醺醺的。】


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接