2011-01-20 7 views
6

En python, puedo hacer lo siguiente para obtener todos los objetos en una lista con una propiedad específica. En este ejemplo me agarra la lista de campos de cada idobj en la lista objs donde obj.id es mayor que 100:Perl equivalente a las comprensiones de la lista de Python con la instrucción if incrustada?

ids = [ obj.id for obj in objs if obj.id > 100] 

¿Cómo puedo hacer lo mismo en Perl? Creo que quiero usar map, pero no sé cómo mapear condicionalmente elementos del conjunto de origen al conjunto de destino.

Respuesta

13

El bloque map puede devolver 0 o más elementos para cada elemento en la lista original. Para omitir un elemento, simplemente devolver la lista vacía ():

my @ids = map { $_->id > 100 ? $_->id :() } @objs; 

Esto supone que los objetos en @objs tienen un atributo id y de acceso asociado. Si quiere tener acceso directo de hash, se puede hacer eso también:

my @ids = map { $_->{id} > 100 ? $_->{id} :() } @objs; 

O, simplemente puede combinar map y grep:

my @ids = map { $_->id } grep { $_->id > 100 } @objs; 

# Or reverse the order to avoid calling $_->id twice: 
my @ids = grep { $_ > 100 } map { $_->id } @objs; 

No estoy seguro de cuál de los que sería más eficiente, pero a menos que @objs sea realmente grande, es poco probable que importe mucho.

Si el valor que está extrayendo del objeto es caro para calcular, a continuación, se puede almacenar en caché el valor para el valor de la prueba y el retorno:

my @vals = map { my $v = $_->expensive_method; $v > 100 ? $v :() } @objs; 
+4

'map + ($ _-> id) x ($ _-> id> 100), @ objs' – ysth

+4

@ysth: Yikes. Creo que salvaría ese para code golf. El operador ternario es mucho más seguro, ya que no tiene que asegurarse de que su condición devuelva solo 0 o 1. – cjm

+1

si tiene dudas, use el operador '() x !!' – ysth

2

Use grep para devolver solo aquellos artículos que coincidan con la condición. Es como filter en otros idiomas.

grep{ condition }@array

Por ejemplo:

my @nums = (1, 50, 7, 105, 200, 3, 1000); 
my @numsover100 = grep { $_ > 100 } @nums; 
foreach my $num (@numsover100) { 
    print $num . "\n"; 
} 
+0

¿Debo emparejar eso con 'map' para obtener lo que quiero? ¿Como realmente quiero que el subcampo de cada uno de los objetos que he 'grep' 'fuera de la lista? –

+0

Sí, así es como lo haría. – Mikel

1

Probablemente se podría obtener con la combinación de map y filter, que es esencialmente lo que hicimos en Python antes de listas por comprensión.

+2

El equivalente de 'filtro 'de Perl se llama' grep'. No es necesario que hagas todo lo posible en tu enlace perlmonks para este problema. – Mikel

0

Utilizando el mapa y grep conjunto pasa por encima de la lista dos veces. Construya el suyo:

sub fancy_filter { 
    my ($map_block, $grep_block, @list) = @_; 
    my @results; 
    foreach my $item (@list) { 
    local $_ = $item; 
    if ($grep_block->()) { 
     push @results, $map_block->(); 
    } 
    } 
    return @results; 
} 

my @ids = fancy_filter(
    sub { $_->{id} },  # map block 
    sub { $_->{id} > 100 }, # grep block 
    @id_list, 
) 
Cuestiones relacionadas