有条件地在列表中包含元素的最佳方法是什么?

时间:2010-02-19 11:53:13

标签: perl list operators

可能的方法:

  1. 使用push

    my @list;  
    push @list, 'foo' if $foo;  
    push @list, 'bar' if $bar;  
    
  2. 使用conditional operator

    my @list = (  
        $foo ? 'foo' : (),    
        $bar ? 'bar' : (),              
    );
    
  3. 使用x!! Boolean list squash operator

    my @list = (  
        ('foo') x!! $foo,  
        ('bar') x!! $bar,  
    );  
    
  4. 哪一个更好,为什么?

4 个答案:

答案 0 :(得分:12)

嗯,他们都做了不同的事情。

然而,所有其他因素相同,

push @list, 'foo' if $foo;

是最直接传达其含义的陈述,因此应该首选。

如果你不得不停下来思考那些应该像推送数组元素一样简单的语句,你就会做错事。

my @list = (
    $foo ? 'foo' : (),
    $bar ? 'bar' : (),
);
如果这是其他地方正在进行的一些巨大初始化的一部分,那么

就可以了。

我认为使用

my @list = (
    ('foo') x!! $foo,
    ('bar') x!! $bar,
); 

表示程序员已经 - 我该怎么把它? - issues

顺便说一下,没有一种称为x!!复合运算符的东西。 !!double logical negation。它将一个任意的false值转换为一个已定义但值为false的值(空字符串),当0期望一个数字时,它会产生perl。真值将转换为1。在这里,!!$foo$bar上运行,并且x!! $foo编写它会不必要地模糊代码的含义。

xrepetition operator。所以,

 ('foo') x !!$foo;

表示重复('foo')一次或根本不重复,具体取决于$foo是真还是假,

PS:当然,事实证明有一个PerlMonks article引入了所谓的布尔列表压缩算子。我看了article并发现它难以令人信服。

答案 1 :(得分:5)

我想建议我将“可读”的方法配音:

sub maybe ($$) { $_[1] ? $_[0] : () }

my @list = (
  maybe("foo", $foo),
  maybe("bar", $bar),
);

它具有这个所谓的x!!运营商的许多好处(虽然稍微长一些),但还有一个额外的好处,就是你可以在以后再回来时理解你的代码。

编辑:原型没有帮助Perl解析任何更好的东西,也不会让我们抛弃括号,它只是阻止Perl做我们不想要的事情。如果我们想抛弃这些问题,我们必须做一些工作。这是一个没有括号的替代版本:

sub maybe {
  my($var, $cond, @rest) = @_;
  return @rest unless $cond;
  return $var, @rest;
}

my @list = (
  maybe "foo", $foo,
  maybe "bar", $bar,
);

无论我们如何处理原型,Perl都会尝试将其解析为maybe("foo", $foo, maybe("bar", $bar)),因此如果我们想要抛弃括号,我们只需要做出正确的结果。

答案 2 :(得分:2)

虽然OP没有要求它,但这个问题为use Benchmark;提供了一个很好的借口

以下是代码:

use strict;
use warnings;
use Benchmark qw( cmpthese);

my $foo = 1;
my $bar = "true";

cmpthese( -10, {
  "push" => sub {
      my @list;
      push @list, 'foo' if $foo;
      push @list, 'bar' if $bar;
  },
  "?::" => sub {
      my @list = (
        $foo ? 'foo' : (),
        $bar ? 'bar' : ()
      );
  },
  "x!!" => sub {
      my @list = (
        ('foo') x!! $foo,
        ('bar') x!! $bar
      );
  }
});

这是一些典型的结果:

         Rate  x!!  ?:: push
x!!  646539/s   --  -8% -12%
?::  701429/s   8%   --  -5%
push 736035/s  14%   5%   --

鉴于brian d foy 7% estimateBenchmark的不确定性,push似乎是最快的成长方式。

总结:

$need4speed ? do { push @like $this} : try { something('less' x!! $obfuscated) };
# DISCLAIMER: This is not valid code

答案 3 :(得分:1)

我个人在这种情况下使用$foo ? 'foo' : (),因为很明显(与('foo') x!! $foo相比),不需要特别努力(与('foo') x !!$foo比较)并且可以在列表上下文中使用(与push @list, 'foo' if $foo;比较)。

但是,当然,答案取决于您选择最佳选项的标准。如果你通过混淆比较它们,('foo') x!! $foo肯定会赢。如果你通过笨拙来比较它们,push @list, 'foo' if $foo;将是第一个。或许你的意思是表现?我猜不是: - )

但如果你用好的风格比较它们,我的选择是$foo ? 'foo' : ()