【问题标题】:What are some elegant features or uses of Perl?Perl 有哪些优雅的特性或用途?
【发布时间】:2010-10-13 02:09:22
【问题描述】:

什么? Perl 漂亮吗? Elegant?他一定是在开玩笑!

确实,那里有一些丑陋的 Perl。有些人,我的意思是很多。我们都见过。

嗯,这是象征汤。不是吗?

是的,有符号。就像“数学”有“符号”一样。只是我们程序员对标准的数学符号比较熟悉。我们逐渐接受了来自我们母语的符号,无论是 ASM、C 还是 Pascal。 Perl 只是决定再添加一些。

好吧,我认为我们应该去掉所有不必要的符号。使代码看起来更好。

这样做的语言已经存在。它被称为Lisp.(很快,perl 6。)

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

哦,假的另一个自我,你真有趣!真的,Perl 可以很漂亮。它也可能很丑陋。使用 Perl,TIMTOWTDI

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

【问题讨论】:

  • 嗯...标签汤? Perl 作为一种编程语言而不是标记语言,没有那么多标签。假设你的意思是“man perlfunc”中的东西,这些都是函数。
  • 说不定是运营商汤...
  • 我的意思是排序 {$a $b} grep { $_ > 0} @{$obj->{key}}。我确切地知道这意味着什么,但是非 ascii 与 ascii 的比例让一些人感到厌烦。 :)
  • 哪一种语言会被扩展为 20 行代码?
  • "non-ascii 与 ascii 的比例"...上次我看的都是 ASCII 字符:{ } = $ _ @ -

标签: perl


【解决方案1】:

我最喜欢的优雅 Perl 特性是它对数值和字符串值使用不同的运算符。

my $string = 1 . 2;
my $number = "1" + "2";
my $unambiguous = 1 . "2";

将此与 JavaScript 等其他动态语言进行比较,其中“+”用于连接和添加。

var string = "1" + "2";
var number = 1 + 2;
var ambiguous = 1 + "2";

或者需要在字符串和数值之间进行类型强制转换的动态语言,例如 Python 和 Ruby。

string = "1" + "2"
number = 1 + 2
throws_exception = 1 + "2"

在我看来,Perl 做得如此正确,而其他语言则做得如此错误。

【讨论】:

    【解决方案2】:

    此文件解析机制紧凑且易于自定义(跳过空行、跳过以 X 开头的行等)。

    open(H_CONFIG, "< $file_name") or die("Error opening file: $file_name! ($!)");
    while (<H_CONFIG>)
    {
       chomp;         # remove the trailing newline
       next if $_ =~ /^\s*$/; # skip lines that are blank
       next if $_ =~ /^\s*#/; # skip lines starting with comments
       # do something with the line
    }
    

    我在不同的构建情况下使用这种类型的构造 - 我需要预处理或后处理负载文件(S 记录等)或 C 文件或收集目录信息以进行“智能构建”。

    【讨论】:

      【解决方案3】:

      我绝对 Black Perl(链接到重写为在 Perl 5 下编译的版本)。它可以编译,但据我所知,它实际上并没有做任何事情。

      这就是语言学家从实用的角度而不是从理论的角度编写的语言所得到的。

      从这个开始,您可以将人们抱怨的 Perl 视为 pidgin Perl(非常有用,但没有表达力,并且要小心尝试在其中表达任何复杂的东西),以及 @pjf 正在谈论的东西作为“适当的”Perl,莎士比亚、海明威、休谟等的语言。 [编辑:错误,虽然比休谟更容易阅读,而且没有莎士比亚那么陈旧。] [重新编辑,希望比海明威少酗酒]

      【讨论】:

        【解决方案4】:

        我最喜欢的一个例子是 Perl 的阶乘计算器实现。在 Perl 5 中,它看起来像这样:

        use List::Util qw/reduce/;
        sub factorial {
            reduce { $a * $b } 1 .. $_[0];
        }
        

        如果数字

        期待 Perl 6,它看起来 like this

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

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

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

        然后像这样在你的代码中使用它:

        my $fact5 = 5!;
        

        【讨论】:

        • 我相信将阶乘函数实现为“!”可以附加到数学符号 12! 的数字上,但我不在 Perl 6 之上,所以我不记得了。
        • 另外,nitpick:给出的 0 值不正确,应该是 1。
        • 你可以用一个简单的 if($x == 0) return 1;甚至是三元运算符($x == 0 ? 1 : [*] 1..$x)。我不知道 Perl 6,但据我所知,其中任何一个都可以工作。
        • perl 6 三元运算符是 ?? !!代替 ? :,但是是的。
        • 使用 ??和 !!让你的 Perl 代码看起来像“WTF”?这让我很开心。
        【解决方案5】:

        如果您有一个逗号分隔的标志列表,并且想要一个查找表,您所要做的就是:

        my %lookup = map { $_ => 1 } split /,/, $flags;
        

        现在您可以像这样简单地测试您需要哪些标志:

        if ( $lookup{FLAG} ) {
            print "Ayup, got that flag!";
        }
        

        【讨论】:

        • 地图保证每个标志始终为 1。
        • 啊,对。对不起,我完全看到之前有一行代码,但没有看。道歉。
        • 对接受命令行参数的程序的一个简单修改是将“split(...)”更改为“@ARGV”。这样,您可以使用“-e”标志的 %lookup{-e} 访问每个标志。命令行中的任何文件都会被传入,但我怀疑你的程序会检查它们,所以没关系。
        【解决方案6】:

        我最喜欢的优雅 Perl 代码并不一定是优雅的。它们是元优雅,可以让您摆脱许多 Perl 开发人员已经养成的所有坏习惯。我需要几个小时或几天的时间才能将它们全部展示在他们应得的细节中,但作为一个简短的列表,它们包括:

        如果你懒得关注链接,我最近做了一个talk at Linux.conf.au 上面的大部分内容。如果你错过了,这里有video of it on-line (ogg theora)。如果你懒得看视频,我今年将在 OSCON 上做一个大大扩展的演讲版本作为教程(标题为 doing Perl right)。

        一切顺利,

        保罗

        【讨论】:

          【解决方案7】:

          “或死”结构:

          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-&gt;(); $body-&gt;() while $continue(你可能指的是$continue-&gt;() ...)更好的是do { $body-&gt;() } while $continue,它达到了保证$body-&gt;()将被执行一次(perldoc.perl.org/perlsyn.html#Statement-Modifiers)的相同效果。
          【解决方案8】:

          我很惊讶没有人提到Schwartzian Transform.

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

          并且在没有 slurp 操作符的情况下,

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

          【讨论】:

          • 那(S.T.)真是太棒了。
          • 并被 Perl 6 淘汰,因为它是内置的!
          • 它只是修复了 perl sort 没有可以缓存的关键函数,只有比较函数的事实。 Python 的排序新关键参数就是这样做的。无论如何,ST 很漂亮,但是来自其他语言的人只会认为 Perl 排序被破坏了,因为需要如此奇怪的咒语才能进行如此常见的操作。
          • 这其实叫decorate-sort-undecorate,起源于Lisp。
          【解决方案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);
          

          【讨论】:

          • Moose 确实使 Perl 成为理想的 OOP 平台 (恕我直言)。
          • 顺便说一句...你也可以这样写... has ['x', 'y'] => (isa => 'Num', is => 'rw');
          • 感谢 draegtun,您的修改使它更性感 ;)
          【解决方案10】:

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

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

          【讨论】:

          • 不幸的是,WMD 编辑器破坏了代码。还有另一个变体适用于:perlmonks.org/index.pl?node_id=45213
          • 我同意这是一部杰作,但当我真正理解它时,我会更加欣赏它。
          • Chris Lutz:这是可以编写的最自我记录的代码。 search.cpan.org/perldoc?Acme::EyeDrops
          • 嗯,它很整洁,但我认为不是很优雅。
          • 我将 替换为 >。希望这将修复错误。
          【解决方案11】:

          加上mapgrep 的喜爱,我们可以编写一个简单的命令行解析器。

          my %opts = map { $_ => 1 } grep { /^-/ } @ARGV;
          

          如果我们愿意,我们可以将每个标志设置为它在@ARGV 中的索引:

          my %opts = map { $ARGV[$_] => $_ } grep { $ARGV[$_] =~ /^-/ } 0 .. $#ARGV;
          

          这样,如果一个标志有一个参数,我们可以得到这样的参数:

          if( defined( $opts{-e} ) ) {
            my $arg = $ARGV[ $opts{-e} ];
            # do -e stuff for $arg
          }
          

          当然,有些人会说我们正在重新发明轮子,我们应该使用 getopt 或其一些变体,但老实说,这是一个相当容易重新发明的轮子。另外,我不喜欢 getopt。

          如果您不喜欢其中的某些行有多长,您总是可以使用中间变量或只是方便的换行符(嘿,Python 狂热者?你听到了吗?我们可以将一行代码放在两行中仍然有效!)让它看起来更好:

          my %opts = map  { $ARGV[$_] => $_   }
                     grep { $ARGV[$_] =~ /^-/ } 0 .. $#ARGV;
          

          【讨论】:

          • 我尽可能多地使用 map 和 grep(而不是 foreach 循环)。我对他们的抱怨是他们没有命名 $_ 参数,如果代码需要引用多个级别的 $_,这很糟糕。
          【解决方案12】:

          有用户希望您的程序处理的文件列表吗?不想意外处理程序、文件夹或不存在的文件?试试这个:

          @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)可用于缩短代码,同时保持可读性。

          【讨论】:

          • 关于warning 的建议让我很痒,因为它需要两次遍历列表。另一方面,虽然很容易将其变成一次通行证,但它变得更像您的不太优雅的列表。想到的可怕的一次性map 版本是map { warn "Not a file: $_" unless -T, $_ } @files
          【解决方案13】:

          Perl 有助于使用列表/哈希来实现命名参数,我认为这非常优雅,并且对自记录代码有很大帮助。

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

          【讨论】:

          • 我得说,这也是我最喜欢的功能之一。很酷的事实:这也将出现在 .NET 中。 :)
          【解决方案14】:

          像我这样的可怜的打字员经常在按 shift 键时抽筋,并且几乎不合理地害怕使用分号,他们开始在python 格式的文件中编写我们的 Perl 代码。 :)

          例如

          >>> k = 5
          >>> reduce(lambda i,j: i*j, range(1,k+1),1)
          120
          >>> k = 0
          >>> reduce(lambda i,j: i*j, range(1,k+1),1)
          1
          

          【讨论】:

          • 减少 { $a * $b } 1 .. $k, 1;比您的 Python 示例少使用一个班次;-P
          • 顺便说一句...我没有否决你,但你的答案被否决并不会让你感到惊讶。
          • 我也讨厌触摸 shift 键。这就是我不再乘以任何东西的原因。考虑解决方法,rmult,需要 4 下划线的 shift 键和任何优雅语言中的“rmult”仍然需要 parens——所以我只是放弃编码。
          • @popcnt - 添加条件以检查 0!是微不足道的,并且在 Perl 中使用 if 的后缀表示法根本不需要 shift 键($varname 除外)——只需执行 "return 0 if $var == 0;"你已经搞定了,只比以前多了 1 个班次。
          • @Chris Lutz:添加一个额外的检查有点抵消了“优雅”,对吧?还有,0! == 1,这就是为什么我喜欢指出它的棘手之处。
          猜你喜欢
          • 1970-01-01
          • 2014-03-05
          • 1970-01-01
          • 2011-04-16
          • 2014-07-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多