Что лучший способ состоит в том, чтобы условно включать элемент в список?

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

  1. Используя нажатие:

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

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

7
задан Sinan Ünür 19 February 2010 в 15:41
поделиться

4 ответа

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

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

push @list, 'foo' if $foo;

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

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

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

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

Я думаю, что использование

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

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

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

x - оператор повторения . Итак,

 ('foo') x !!$foo;

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

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

12
ответ дан 6 December 2019 в 09:19
поделиться

Я хотел бы предложить подход, который я назову "Читабельным":

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

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

Он имеет многие преимущества этого предполагаемого x!!! оператора (хотя и немного длиннее). оператора (хотя и немного длиннее), но с тем дополнительным бонусом, что вы можете действительно понять свой код, когда вернетесь к нему позже.

EDIT: Прототип не помогает 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)), поэтому, если мы хотим избавиться от скобок, мы просто должны сделать так, чтобы это давало правильный результат.

5
ответ дан 6 December 2019 в 09:19
поделиться

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

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

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

1
ответ дан 6 December 2019 в 09:19
поделиться

Хотя OP не требовал этого, эта проблема дала мне хороший повод использовать 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 , сделанную Брайаном Дфоем для неопределенности, определенно кажется, что толчок является самый быстрый способ расти.

Подведение итогов:

$need4speed ? do { push @like $this} : try { something('less' x!! $obfuscated) };
# DISCLAIMER: This is not valid code
2
ответ дан 6 December 2019 в 09:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: