Каков наилучший способ условно включить элемент в список? - PullRequest
7 голосов
/ 19 февраля 2010

Возможные пути:

  1. Использование push :

    my @list;  
    push @list, 'foo' if $foo;  
    push @list, 'bar' if $bar;  
    
  2. Использование условного оператора :

    my @list = (  
        $foo ? 'foo' : (),    
        $bar ? 'bar' : (),              
    );
    
  3. Использование оператора сквоша x!! в логическом списке :

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

Какой из них лучше и почему?

Ответы [ 4 ]

12 голосов
/ 19 февраля 2010

Ну, они все делают разные вещи.

Однако при прочих равных условиях

push @list, 'foo' if $foo;

- это утверждение, которое наиболее точно передает его значение, поэтому оно должно быть предпочтительным.

Если вам нужно остановиться и подумать об утверждении, которое предположительно делает что-то такое же простое, как нажатие на элемент массива, вы делаете что-то не так.

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

может быть в порядке, если это часть колоссальной инициализации, которая выполняется в другом месте.

Я думаю, используя

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

указывает, что у программиста есть & mdash; как мне это выразить? & mdash; вопросы .

Между прочим, не существует такого понятия, как x!! составной оператор. !! - это двойное логическое отрицание . Он преобразует произвольное ложное значение в определенное, но ложное значение (пустую строку), которое дает 0 при использовании, где perl ожидает число. Истинное значение преобразуется в 1. Здесь !! работает с $foo и $bar, и запись его x!! $foo излишне запутывает смысл кода.

x является оператором повторения . Таким образом,

 ('foo') x !!$foo;

означает повторить ('foo') один раз или не сделать это вообще, в зависимости от того, истинно ли $foo или ложно, соответственно .

PS: Конечно, оказывается, в статье PerlMonks вводится так называемый оператор сквоша логического списка . Я прочитал статью и нашел ее неубедительной.

5 голосов
/ 19 февраля 2010

Я хотел бы предложить то, что я буду называть «читабельным» подходом:

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 голосов
/ 20 февраля 2010

Хотя ОП не требовал этого, эта проблема дала мне хорошее оправдание 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%   --

Учитывая оценку 7% Брайана Д Фоя для неопределенности Benchmark, определенно кажется, что push - самый быстрый способ роста.

Подводя итог:

$need4speed ? do { push @like $this} : try { something('less' x!! $obfuscated) };
# DISCLAIMER: This is not valid code
1 голос
/ 20 февраля 2010

Лично я использую $foo ? 'foo' : () в таких случаях, поскольку он понятен (по сравнению с ('foo') x!! $foo), не требует особых усилий для понимания (по сравнению с ('foo') x !!$foo) и может использоваться в контексте списка (по сравнению с push @list, 'foo' if $foo;).

Но, конечно, ответ зависит от того, по каким критериям вы выбираете вариант best .Если вы сравните их по путанице, ('foo') x!! $foo обязательно победит.Если вы сравните их по неуклюжести, push @list, 'foo' if $foo; будет первым.Или, может быть, вы имели в виду производительность?Наверное, нет: -)

Но если вы сравните их по хорошему стилю, я выберу $foo ? 'foo' : ().

...