2011-08-25 7 views
5

Mi spiace, sembra una domanda così basilare, ma ancora non capisco. Se ho un hash, ad esempio:Perché Perl pensa che esista un elemento hash multi-livello inesistente?

my %md_hash =(); 
$md_hash{'top'}{'primary'}{'secondary'} = 0; 

Come mai questo è vero?

if ($md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I'm true even though I'm not in that hash\n"; 
} 

Non c'è un livello "foobar" nell'hash, quindi non dovrebbe risultare falso?

TIA

risposta

6

Prova una ricerca su "Perl autovivificazione".

I valori hash "sorgono" quando li si accede per la prima volta. In questo caso, il valore è undef, che quando interpretato come un numero è zero.

per verificare l'esistenza di un valore hash, senza auto-vivificante, utilizzare l'operatore exists:

if (exists $md_hash{'top'}{'foobar'}{'secondary'} 
    && $md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I exist and I am zero\n"; 
} 

Si noti che questo sarà ancora auto-vivificare $md_hash{'top'} e $md_hash{'top'}{'foobar'} (cioè sub-hash).

[modifica]

Come tchrist sottolinea in un commento, è lo stile povero per confrontare undef contro qualsiasi cosa. Quindi un modo migliore per scrivere questo codice sarebbe:

if (defined $md_hash{'top'}{'foobar'}{'secondary'} 
    && $md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I exist and I am zero\n"; 
} 

(. Anche se questo sarà ora auto-vivificare tutti e tre i livelli del hash nidificato, impostando il livello più basso a undef')

+0

Fantastico !! Il tuo esempio ha risolto il mio problema e il tuo consiglio mi ha portato a http://www.perlmonks.org/?node=Autovivification, ora lo so !! Grazie! – Analog

+0

@Analog: Ma questa non è la risposta giusta. La risposta corretta non è applicare test di uguaglianza numerica rispetto a valori non definiti. – tchrist

+0

@tchrist: Quindi ogni affermazione che ho fatto è stata corretta al 100%, e ho risolto il problema dell'OP, ma poiché ho violato il tuo personale senso dello stile hai ridimensionato la mia risposta. Ho capito bene? Classy! – Nemo

7

si esegue il test un valore indefinito per lo zero numerico. Certo che ottieni un risultato vero! Cosa ti aspettavi?

È inoltre necessario ricevere un avviso in use warnings. Perché non l'hai fatto?

Se non si avvia un programma con:

use v5.12; # or whatever it is you are using 
use strict; 
use warnings; 

davvero non dovrebbe nemmeno. :)


EDIT

NB: Sto solo chiarendo la correttezza, perché le righe di commento non sono buone per questo. Non potevo davvero preoccuparmi di lamentarsi di meno dei punti reputazione. Voglio solo che la gente capisca.

Anche sotto il modulo CPAN autovivification, non cambia nulla. Testimone:

use v5.10; 
use strict; 
use warnings; 
no autovivification; 

my %md_hash =(); 

$md_hash{top}{primary}{secondary} = 0; 

if ($md_hash{top}{foobar}{secondary} == 0) { 
    say "yup, that was zero."; 
} 

Quando eseguito, che dice:

$ perl /tmp/demo 
Use of uninitialized value in numeric eq (==) at /tmp/demo line 10. 
yup, that was zero. 

Il test è l'operatore ==. Il suo RHS è 0.Il suo LHS è undefindipendentemente dall'autovettura. Poiché undef è numericamente 0, ciò significa che sia LHS che RHS contengono 0, che lo == identifica correttamente come contenente lo stesso numero.

L'autovivificazione non è il problema qui, come giustamente osserva ysth quando scrive che "Questa non è una domanda specifica di hash multidimensionale". Tutto ciò che conta è ciò che si passa a ==. E undef è numericamente 0.

È possibile interrompere autoviv se lo si desidera, davvero - utilizzando il pranma CPAN. Ma non riuscirai mai a cambiare ciò che accade qui sopprimendo autoviv. Ciò dimostra che non è affatto una questione di autoviv, solo uno undef.

Ora, si ottenere i tasti "extra" quando si esegue questa operazione, poiché il valore non definito verrà compilato nel percorso attraverso la catena di deferimento. Questi sono neccesarily tutti uguali:

$md_hash{top}{foobar}{secondary}   # implicit arrow for infix deref 
$md_hash{top}->{foobar}->{secondary}  # explicit arrow for infix deref 
${ ${ $md_hash{top} }{foobar} }{secondary} # explicit prefix deref 

Ogni volta che si Deref un lvaluable undef in Perl, che posizione di archiviazione sempre si riempie con il corretto tipo di riferimento ad un referente anonima appena allocato del tipo corretto. In breve, ha un'autovettura.

E che si può interrompere eliminando o spostando l'autoviv. Tuttavia, negare l'autoviv non equivale a sottrarlo, perché basta cambiare il tipo di oggetto che viene restituito. La spesa complessiva è ancora completamente valutata: non c'è il cortocircuito automatico solo perché si sopprime l'autoviv. Questo perché autoviv non è il problema (e se non fosse lì, saresti davvero seccato: credimi).

Se si desidera cortocircuitare, è necessario scriverlo da soli. Non ho mai bisogno di me stesso. In Perl, cioè. D'altra parte, i programmatori C sono abbastanza abituato a scrivere

if (p && p->whatever) { ... } 

e così si può fare anche quello, se si vuole. Tuttavia, è abbastanza raro nella mia esperienza personale. Devi quasi piegarti in modo errato in Perl per fare sempre la differenza, dato che è abbastanza facile organizzare il tuo codice non per cambiare il modo in cui agisce se ci sono livelli vuoti.

+0

re 'sempre', non così. ed è difficile stabilire la regola generale; le cose vengono autovivificate quando non è fastidioso farlo, il che è più o meno quando si verifica un tipo di operazione di modifica. Confronta 'my $ foo; 1 se @ $ pippo; stampa $ pippo' a 'mio $ pippo; undef @ $ foo; stampa $ pippo'. – ysth

+0

Se questo è diverso da quello che ricordi, potrebbe essere perché tu e Larry avete lasciato p5p per così tanti anni, facendo sì che venissero prese delle decisioni che potreste essere in disaccordo se vi foste trovato (caso per caso, la rimozione di 'pacchetto; ') – ysth

+2

@ysth: non ho mai lasciato p5p. Ho semplicemente saltato la lettura per diversi anni. Ho ancora tutto nella mia partizione di posta. :) – tchrist

7

Questa non è una domanda specifica di hash multidimensionale.

funziona lo stesso con

my %foo; 
if ($foo{'bar'} == 0) { 
    print "I'm true even though I'm not in that hash\n"; 
} 

$foo{'bar'} è indefinito, che mette a confronto fedele a 0, anche se con un messaggio di avviso se si dispone di avvisi attivati ​​come si dovrebbe.

C'è un ulteriore effetto collaterale nel tuo caso; quando si dice

my %md_hash =(); 
$md_hash{'top'}{'primary'}{'secondary'} = 0; 

if ($md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I'm true even though I'm not in that hash\n"; 
} 

$md_hash{'top'} restituisce un riferimento hash, e il tasto 'foobar' viene cercato in tale hash. A causa di {'secondary'}, quella ricerca di elementi "foobar" è nel contesto di hash-dereference.Questo rende $md_hash{'top'}{'foobar'} "autovivify", un riferimento hash come il valore della chiave 'foobar', lasciando con questa struttura:

my %md_hash = (
    'top' => { 
     'primary' => { 
      'secondary' => 0, 
     }, 
     'foobar' => {}, 
    }, 
); 

Il pragma autovivificazione può essere utilizzato per disattivare questo comportamento. Le persone a volte affermano che esiste() ha qualche effetto sull'autovifificazione, ma questo non è vero.

+1

Sì, esattamente. Questo non è un problema di autoviv. È un problema "Ho dimenticato di usare gli avvisi". Ho * mai * avuto autoviv essere un problema, ma ho spesso avuto undef un problema. Autoviv non ha mai causato un bug nel mio codice, mai. Solo l'abuso di test definiti su contenuti indefiniti ha. – tchrist

+0

Sigh, sai che l'ho fatto e non ho nemmeno notato "l'uso del valore non inizializzato in concatenazione (.) O stringa", stavo stampando così tante altre schifezze. Maggiori fallimenti da parte mia. – Analog