2010-02-19 17 views
7

maneras posibles:¿Cuál es la mejor manera de incluir condicionalmente un elemento en una lista?

  1. Uso push:

    my @list; 
    push @list, 'foo' if $foo; 
    push @list, 'bar' if $bar; 
    
  2. Utilizando el conditional operator:

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

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

¿Cuál es mejor y por qué?

+2

Sólo una nota, que no está usando alguna de estas cadenas como referencias de variables, ¿verdad? No '$ {$ list [0]}'? Esto se parece sospechosamente a un hash para mí. –

+0

Las preguntas del tipo de encuesta deberían CW. –

+0

@Chris: No. Usé cadenas solo por simplicidad. –

Respuesta

12

Bueno, todos hacen cosas diferentes.

Sin embargo, todos los demás factores son iguales,

push @list, 'foo' if $foo; 

es la declaración que transmite su significado más directamente, por lo que debe preferirse.

Si tiene que pausar y pensar acerca de una declaración que supuestamente hace algo tan simple como pulsar un elemento de matriz, está haciendo algo mal.

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

podría estar bien si esto es parte de alguna inicialización colosal que se está realizando en otro lugar.

Creo que usando

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

indica que el programador tiene — cómo puedo poner esto?issues.

Por cierto, no existe el operador compuesto x!!. El !! es double logical negation. Convierte un valor falso arbitrario a un valor definido pero falso (la cadena vacía) que produce 0 cuando se usa donde perl espera un número. Un valor verdadero se convierte a 1. Aquí, !! opera en $foo y $bar y escribirlo x!! $foo ofusca innecesariamente el significado del código.

x es el repetition operator. Así,

('foo') x !!$foo; 

significa repetición ('foo') una vez o ninguna en absoluto, dependiendo de si $foo es verdadera o falsa, respectivamente.

PD: Por supuesto, resultó que había un PerlMonks article que introducía el llamado list boolean list squash operator. Leí el article y no me pareció convincente.

+2

De acuerdo, 'x !!' es un poco demasiado inteligente para mi gusto. Espero que no gane tracción como modismo. –

+0

En realidad '!!' convierte un valor falso en una cadena de longitud cero. –

+0

@Brad que produce '0' cuando se usa en un lugar donde' perl' espera un número. Corregido ahora. Gracias. –

5

me gustaría sugerir lo que voy a copiar el enfoque "legible":

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

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

Tiene muchas de las ventajas de esta supuesta x!! operador (aunque un poco más largo), pero con la ventaja añadida de que en realidad puedes entender tu código cuando vuelvas más tarde.

EDITAR: El prototipo no ayuda a Perl a analizar mejor y no nos permite abandonar el paréntesis, solo evita que Perl haga cosas que no queremos. Si queremos deshacernos de los parens, tenemos que hacer algo de trabajo. Aquí hay una versión alternativa que funciona sin paréntesis:

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

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

No importa lo que hacemos con prototipos, Perl intentará analizar como maybe("foo", $foo, maybe("bar", $bar)), por lo que si queremos abandonar el paréntesis, sólo tenemos que hacer que le dan al resultado correcto

+0

+1 Además, los paréntesis alrededor de las args de Maybe son opcionales debido a proto. –

+0

@eugene y - Es por eso que utilicé un prototipo, pero es incorrecto. Sin paréntesis, intentaría analizar como 'quizás (' foo ', $ foo, quizás (' 'bar' ', $ bar)) '.El prototipo lo rechaza, asegurándose de que Perl no intente hacer algo que no queremos que haga. Por supuesto, con un poco de trabajo podríamos hacerlo funcionar sin paréntesis. –

+0

Gracias por corregirme. Se puede usar 'maybe (" foo ", $ foo)' o '(tal vez" foo ", $ foo)'. La versión sin prototipo parece ser un exceso para mí :) –

1

Personalmente utilizo $foo ? 'foo' :() en tales casos, ya que es evidente (comparar a ('foo') x!! $foo), no requiere un esfuerzo particular de entenderse (en comparación con ('foo') x !!$foo) y puede ser utilizado en el contexto de la lista (en comparación con push @list, 'foo' if $foo;).

Pero, por supuesto, la respuesta depende de qué criterio elijas para elegir la opción mejor. Si los compara por ofuscación, ('foo') x!! $foo seguramente ganará. Si los compara por torpeza, push @list, 'foo' if $foo; será el primero. ¿O quizás quisiste decir performance? Supongo que no :-)

Pero si las comparas por buen estilo, mi elección es $foo ? 'foo' :().

2

Aunque el PO no llamó a él, este problema me dio una buena excusa para use Benchmark;

Aquí está el código:

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

Y aquí están algunos resultados típicos:

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

Dada la 7% estimate de brian d foy para la incertidumbre de Benchmark, definitivamente parece que push es la forma más rápida de crecer.

Resumiendo:

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