简体   繁体   English

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

[英]What's the best way to conditionally include an element in a list?

Possible ways: 可能的方式:

  1. Using push : 使用push

     my @list; push @list, 'foo' if $foo; push @list, 'bar' if $bar; 
  2. Using the conditional operator : 使用条件运算符

     my @list = ( $foo ? 'foo' : (), $bar ? 'bar' : (), ); 
  3. Using the x!! 使用x!! Boolean list squash operator : 布尔列表壁球运算符

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

Which one is better and why? 哪一个更好,为什么?

Well, they all do different things. 好吧,他们都做不同的事情。

However, all other factors being equal, 但是,在所有其他因素都相同的情况下,

push @list, 'foo' if $foo;

is the statement that conveys its meaning most directly, so it should be preferred. 是最直接传达其含义的语句,因此应首选。

If you have to pause and think about statement that supposedly does something as simple as pushing an array element, you are doing something wrong. 如果您不得不停下来想一想应该执行与推送数组元素一样简单的语句,那么您在做错了什么。

my @list = (
    $foo ? 'foo' : (),
    $bar ? 'bar' : (),
);

could be OK if this is part of some colossal initialization that is being done elsewhere. 如果这是在其他地方进行的一些巨大初始化的一部分,则可以确定。

I think using 我认为使用

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

indicates that the programmer has — how can I put this? 表示程序员拥有- 我该如何放置? issues . - 问题

Incidentally, there is no such thing called the x!! 顺便说一句,没有所谓的x!! composite operator. 复合运算符。 The !! !! is double logical negation . 双重逻辑否定 It converts an arbitrary false value to a defined but false value (the empty string) which yields 0 when used where perl expects a number. 它将任意的false值转换为已定义但为false的值(空字符串),该值在perl需要数字的地方使用时将产生0 A true value is converted to a 1 . 真值将转换为1 Here, !! 在这里!! operates on $foo and $bar and writing it x!! $foo $foo$bar并将其编写为x!! $foo x!! $foo unnecessarily obfuscates the meaning of the code. x!! $foo不必要地混淆了代码的含义。

x is the repetition operator . x重复运算符 So, 所以,

 ('foo') x !!$foo;

means repeat ('foo') once or not at all, depending on whether $foo is true or false, respectively . 表示一次重复('foo')一次,这取决于$foo是true还是false

PS: Of course, it turns out there was a PerlMonks article introducing the so-called boolean list squash operator . PS:当然,结果是有一篇PerlMonks文章介绍了所谓的布尔列表壁球运算符 I read the article and find it unconvincing. 我阅读了这篇文章 ,发现它没有说服力。

I'd like to suggest what I shall dub the "Readable" approach: 我想建议我对“可读”方法进行配音:

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

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

It has many of the benefits of this alleged x!! 它有许多所谓的x!!的好处x!! operator (though slightly longer), but with the added bonus that you can actually understand your code when you come back to it later. 运算符(虽然稍长一些),但是有了额外的好处,您稍后可以真正理解您的代码。

EDIT: The prototype doesn't help Perl parse any better and doesn't let us ditch the parenthesis, it just prevents Perl from doing things we don't want. 编辑:原型不能帮助Perl更好地解析,也不能让我们放弃括号,它只是防止Perl做我们不想要的事情。 If we want to ditch the parens, we have to do some work. 如果要抛弃父母,我们必须做一些工作。 Here's an alternate version that works without parenthesis: 这是不带括号的替代版本:

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

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

No matter what we do with prototypes, Perl will try to parse it as maybe("foo", $foo, maybe("bar", $bar)) , so if we want to ditch the parenthesis, we just have to make that give the correct result. 无论我们使用原型做什么,Perl都将尝试将其解析为maybe("foo", $foo, maybe("bar", $bar)) ,因此,如果我们想抛开括号,我们只需要使它成为括号即可。给出正确的结果。

Although the OP didn't call for it, this problem provided me with a good excuse to use Benchmark; 尽管OP并没有要求这样做,但是这个问题为我提供了use Benchmark;的很好的借口use Benchmark;

Here's the code: 这是代码:

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

And here are some typical results: 以下是一些典型结果:

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

Given brian d foy's 7% estimate for Benchmark 's uncertainty, it definitely seems like push is the fastest way to grow. 鉴于brian d foy对Benchmark的不确定性的估计7% ,显然push似乎是最快的增长方式。

Summing up: 加起来:

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

Personally I use $foo ? 'foo' : () 我个人使用$foo ? 'foo' : () $foo ? 'foo' : () in such cases, since it is clear (comparing to ('foo') x!! $foo ), doesn't require particular effort to be understood (comparing to ('foo') x !!$foo ) and can be used in the list context (comparing to push @list, 'foo' if $foo; ). $foo ? 'foo' : ()在这种情况下,因为很明显(与('foo') x!! $foo ),不需要特别的努力就可以理解(与('foo') x !!$foo比较('foo') x !!$foo ),并且可以在列表上下文中使用(与push @list, 'foo' if $foo; )。

But, of course, the answer depends on what criteria you choose to choose the best option. 但是,当然,答案取决于您选择哪种标准来选择最佳选项。 If you compare them by obfuscation, ('foo') x!! $foo 如果通过混淆比较它们,则('foo') x!! $foo ('foo') x!! $foo will surely win. ('foo') x!! $foo一定会赢。 If you compare them by clumsiness, push @list, 'foo' if $foo; 如果比较笨拙, push @list, 'foo' if $foo; will be the first. 将是第一个。 Or maybe you meant performance? 还是您的意思是性能? I guess not :-) 我猜不会 :-)

But if you compare them by good style, my choice is $foo ? 'foo' : () 但是,如果按风格比较它们,我的选择是$foo ? 'foo' : () $foo ? 'foo' : () . $foo ? 'foo' : ()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM