What is the best way to conditionally include an item in a list?

Possible ways:

  • Using push :

    my @list; push @list, 'foo' if $foo; push @list, 'bar' if $bar; 
  • Using the conditional operator :

     my @list = ( $foo ? 'foo' : (), $bar ? 'bar' : (), ); 
  • Using x!! Boolean list buffer operator :

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

Which one is better and why?

+7
operators list perl
source share
4 answers

Well, they all do different things.

However, all other factors are equal,

 push @list, 'foo' if $foo; 

- This is a statement that directly conveys its meaning, so it should be preferred.

If you need to pause and think about a statement that supposedly does something as simple as clicking an array element, you are doing something wrong.

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

it might be OK if it is part of some colossal initialization that runs elsewhere.

I think using

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

indicates what the programmer has - how can I put this? - problems .

By the way, there is no such thing called the composite operator x!! . !! double logical negation . It converts an arbitrary false value to a specific but false value (empty string), which gives 0 when used where perl expects a number. The true value is converted to 1 . Here !! works on $foo and $bar , and its entry is x!! $foo x!! $foo unnecessarily confuses the meaning of the code.

x is a repetition operator. Thus,

  ('foo') x !!$foo; 

means repeat ('foo') one or more times, depending on whether $foo true or false, respectively.

PS: Of course, it turned out that there is an article in the PerlMonks article that introduces the so-called Boolean list squash operator. I read the article and found it inconclusive.

+12
source share

I would suggest that I call the “Slave” approach:

 sub maybe ($$) { $_[1] ? $_[0] : () } my @list = ( maybe("foo", $foo), maybe("bar", $bar), ); 

It has many advantages of this putative operator x!! (although a little longer), but with an added bonus, which you can really understand when you return to it later.

EDIT: The prototype does not help Perl to understand better and does not allow us to put brackets, it just prevents Perl from doing what we do not want. If we want to snatch partners, we have to do some work. Here's an alternate version that works without parentheses:

 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 the prototypes, Perl will try to parse it like maybe("foo", $foo, maybe("bar", $bar)) , so if we want to cut out the brackets, we just need to do to give correct result.

+5
source share

Although the OP did not cause this, this problem gave me a good excuse to use Benchmark;

Here is 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 a brian d foy score of 7% for Benchmark uncertainty, it definitely seems that push is the fastest way to grow.

Summarizing:

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

Personally, am I using $foo ? 'foo' : () $foo ? 'foo' : () in such cases, since it is clear (compared to ('foo') x!! $foo ), it does not require much effort to understand (compared to ('foo') x !!$foo ) and can be used in the context of a list (compared to push @list, 'foo' if $foo; ).

But, of course, the answer depends on what criteria you choose in order to choose the best option. If you compare them with obfuscation, ('foo') x!! $foo ('foo') x!! $foo will surely win. If you compare them to inconvenience, push @list, 'foo' if $foo; will be the first. Or maybe you mean performance? I think not :-)

But if you compare them in a good style, my choice is $foo ? 'foo' : () $foo ? 'foo' : () .

+1
source share

All Articles