2012-01-20 18 views
7

Tarea: construir hash usando map, donde las claves son los elementos de la matriz dada @a, y los valores son primeros elementos de la lista devueltos por alguna función f ($ element_of_a):Perl: asignación al primer elemento de listas

my @a = (1, 2, 3); 
my %h = map {$_ => (f($_))[0]} @a; 

Todo el bien hasta que f() devuelve una lista vacía (que es absolutamente correcto para f(), y en ese caso me gustaría asignar UNDEF). El error podría ser reproducida con el siguiente código:

my %h = map {$_ =>()[0]} @a; 

el error en sí suena como "número impar de elementos en la asignación de hash". Cuando vuelvo a escribir el código de tal manera que:

my @a = (1, 2, 3); 
my $s =()[0]; 
my %h = map {$_ => $s} @a; 

o

my @a = (1, 2, 3); 
my %h = map {$_ => undef} @a; 

Perl no se queja en absoluto.

Entonces, ¿cómo debo resolver esto - obtener los primeros elementos de la lista devueltos por f(), cuando la lista devuelta está vacía?

versión de Perl es 5.12.3

Gracias.

+3

Wrap la llamada a 'F' de manera que cuando se devuelve una lista vacía, se suministran' undef' o de lo contrario el primer elemento de la lista que regresó. –

Respuesta

7

Acabo de jugar un poco, y parece que ()[0], en contexto de lista, se interpreta como una lista vacía en lugar de como un escalar undef. Por ejemplo, esto:

my @arr =()[0]; 
my $size = @arr; 
print "$size\n"; 

imprime 0. Entonces $_ =>()[0] es aproximadamente equivalente a solo $_.

Para solucionarlo, puede utilizar la función scalar para forzar contexto escalar:

my %h = map {$_ => scalar((f($_))[0])} @a; 

o puede adjuntar una explícita undef al final de la lista:

my %h = map {$_ => (f($_), undef)[0]} @a; 

o puede hacer ajuste el valor de retorno de su función en una matriz real (en lugar de solo una lista plana):

my %h = map {$_ => [f($_)]->[0]} @a; 

(me gusta que la última opción mejor, personalmente.)


El comportamiento especial de una rebanada de una lista vacía se documenta bajo “Slices” in perldata:

Una rebanada de una lista vacía sigue siendo una lista vacía.[...] Esto hace que sea fácil de escribir bucles que terminan cuando se devuelve una lista nula:

while (($home, $user) = (getpwent)[7,0]) { 
    printf "%-8s %s\n", $user, $home; 
} 
+1

He editado en una cita la documentación que explica por qué '() [0]' devuelve una lista vacía en lugar de undef. Si no aprueba, no dude en revertir mi edición (o mejor aún, mejorarla). – derobert

+0

@derobert: I * fuertemente * apruebo. ¡Muchas gracias! – ruakh

+0

No hay nada malo con el análisis aquí, pero eso es mucho ruido de línea. Yo personalmente preferiría tener 'f' para manejar el caso extremo ya que hace que el código sea más fácil de mantener de esa manera. Por supuesto, si no hay control sobre la definición de 'f', entonces eso es totalmente diferente – Zaid

0

Me segunda sugerencia de Jonathan Leffler - la mejor cosa a hacer sería la de resolver el problema desde la raíz si al es posible:

sub f { 

    # ... process @result 

    return @result ? $result[0] : undef ; 
} 

el explícita undef es necesario que el problema de lista vacía a ser eludido.

0

Al principio, ¡muchas gracias por todas las respuestas! Ahora siento que debo proporcionar los detalles reales de la tarea real.

estoy analizar un archivo XML que contiene el conjunto de elemento de cada uno que se parece a:

<element> 
    <attr_1>value_1</attr_1> 
    <attr_2>value_2</attr_2> 
    <attr_3></attr_3> 
</element> 

Mi objetivo es crear Perl hash de elemento que contiene las siguientes claves y valores:

('attr_1' => 'value_1', 
'attr_2' => 'value_2', 
'attr_3' => undef) 

Echemos un vistazo más de cerca al elemento <attr_1>. XML::DOM::ParserCPAN módulo que utilizo para el análisis crea para ellos un objeto de la clase XML::DOM::Element, vamos a dar el nombre $attr para su referencia. El nombre del elemento se puso fácil por $attr->getNodeName, pero para acceder al texto entre <attr_1> etiquetas que uno tiene que recibir todos los elementos secundarios 's la <attr_1> en un primer momento:

my @child_ref = $attr->getChildNodes; 

Para <attr_1> y <attr_2> elementos ->getChildNodes devuelve una lista que contiene exactamente una referencia (al objeto de la clase XML::DOM::Text), mientras que para <attr_3> devuelve una lista vacía. Para el <attr_1> y <attr_2> debería obtener el valor $child_ref[0]->getNodeValue, mientras que para <attr_3> debo colocar undef en el hash resultante ya que no contiene elementos de texto.

Así que vemos que (método ->getChildNodes en la vida real) f de la función de aplicación no pudo ser controlado :-) El código resultante que he escrito está (la subrutina se proporciona con la lista de referencias para los elementos XML::DOM::Element<attr_1>, <attr_2>, y <attr_3>):

sub attrs_hash(@) 
{ 
    my @keys = map {$_->getNodeName} @_; # got ('attr_1', 'attr_2', 'attr_3') 
    my @child_refs = map {[$_->getChildNodes]} @_; # got 3 refs to list of XML::DOM::Text objects 
    my @values = map {@$_ ? $_->[0]->getNodeValue : undef} @child_refs; # got ('value_1', 'value_2', undef) 

    my %hash; 
    @hash{@keys} = @values; 

    %hash; 
} 
+0

Ojalá hubieras mencionado esto por adelantado. Solo obtendrá una respuesta tan buena como la pregunta que haga. Lástima que esta información no haya estado disponible antes. – Zaid

+0

¿Por qué? Supongo que tengo las respuestas perfectas que me permitieron aclarar muchos puntos sobre listas y sectores :-) – indexless

Cuestiones relacionadas