2012-01-20 13 views
7

Compito: creare un hash usando la mappa, dove le chiavi sono gli elementi dell'array dato @a, e i valori sono gli primi elementi restituiti da qualche funzione f ($ element_of_a):Perl: mapping al primo elemento degli elenchi

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

Tutto il bene fino a quando f() restituisce un elenco vuoto (che è assolutamente corretto per f(), e in quel caso mi piacerebbe assegnare undef). L'errore potrebbe essere riprodotto con il seguente codice:

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

l'errore stesso suona come "numero dispari di elementi in assegnazione hash". Quando ho riscrivere il codice in modo tale che:

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 non si lamenta affatto.

Quindi, come devo risolvere questo: ottenere i primi elementi dell'elenco restituiti da f(), quando l'elenco restituito è vuoto?

Perl versione è 5.12.3

Grazie.

+3

Avvolgere la chiamata a 'f' in modo che quando si restituisce una lista vuota, fornite' undef' o in altro modo il primo elemento della lista è tornato. –

risposta

7

Ho appena giocato un po 'e sembra che ()[0], nel contesto dell'elenco, sia interpretato come una lista vuota piuttosto che come uno scalare undef. Ad esempio, questo:

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

stampe 0. Quindi $_ =>()[0] equivale approssimativamente a $_.

Per risolvere il problema, è possibile utilizzare la funzione scalar per forzare un contesto scalare:

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

oppure si può aggiungere un esplicito undef alla fine della lista:

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

oppure si può racchiudere il valore restituito della funzione in un array vero (anziché solo un elenco semplice):

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

(mi piace che ultima opzione migliore, personalmente.)


Il comportamento speciale di una fetta di una lista vuota è documentato in “Slices” in perldata:

Una fetta di una lista vuota è ancora un lista vuota.[...] In questo modo è facile scrivere cicli che si interrompono quando viene restituita una lista nulla:

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

Ho editato in cita alla documentazione che spiega perché '() [0]' restituisce una lista vuota invece di undef. Se non approvi, non esitare a ripristinare la mia modifica (o, meglio ancora, a migliorarla). – derobert

+0

@derobert: I * fortemente * approva. Grazie mille! – ruakh

+0

Qui non c'è niente di sbagliato nell'analisi, ma c'è molto rumore di linea. Personalmente preferirei avere "f" per gestire il caso edge in quanto rende il codice più manutenibile in quel modo. Naturalmente, se non c'è alcun controllo sulla definizione di 'f', allora è un'altra questione. – Zaid

0

ho secondo suggerimento di Jonathan Leffler - la cosa migliore da fare sarebbe quella di risolvere il problema dalla radice se non del tutti i possibili:

sub f { 

    # ... process @result 

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

il esplicito undef è necessario per il problema lista vuota per essere aggirata.

0

In un primo momento, molte grazie per tutti i repeater! Ora sento che dovrei fornire i dettagli reali del compito reale.

che sto analisi di un file XML che contiene l'insieme di elemento di ciascun sembra che:

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

Il mio obiettivo è quello di creare Perl hash per elemento che contiene le seguenti chiavi e valori:

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

Diamo uno sguardo più da vicino all'elemento <attr_1>. XML::DOM::ParserCPAN modulo che utilizzo per l'analisi crea per loro un oggetto di classe XML::DOM::Element, diamo il nome $attr come riferimento. Il nome di elemento è ottenuto facile da $attr->getNodeName, ma per l'accesso al testo racchiuso tra <attr_1> tag uno deve ricevere tutti gli elementi figlio del <attr_1> s' in un primo momento:

my @child_ref = $attr->getChildNodes; 

Per <attr_1> e <attr_2> elementi ->getChildNodes restituisce una lista contenente esattamente un riferimento (all'oggetto della classe XML::DOM::Text), mentre per <attr_3> restituisce una lista vuota. Per lo <attr_1> e <attr_2> dovrei ottenere il valore $child_ref[0]->getNodeValue, mentre per <attr_3> dovrei inserire undef nell'hash risultante poiché non contiene elementi di testo.

Così si vede che (metodo ->getChildNodes nella vita reale) f della funzione implementazione non poteva essere controllata :-) Il codice risultante che ho scritto è (la subroutine viene fornito con la lista dei XML::DOM::Element riferimenti per gli elementi <attr_1>, <attr_2>, e <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

Vorrei che lo avessi menzionato in anticipo. Riceverai solo una risposta valida come la domanda che fai. Peccato che questa informazione non sia stata resa disponibile prima. – Zaid

+0

Perché? Suppongo di avere le risposte perfette che mi hanno permesso di chiarire molti punti riguardanti liste e sezioni :-) – indexless

Problemi correlati