2010-04-27 13 views
5

Ho bisogno di convalidare un hash Perl di elemento hash come $Table{$key1}{$key2} per esistere ed essere definito. Ecco cosa faccio. (Non ho idea $key1 esiste anche)Esiste un modo semplice per convalidare un hash dell'elemento hash esistente ed è definito?

if 
((defined $Table{$key1}) && 
(exists $Table{$key1}) && 
(defined $Table{$key1}{$key2}) && 
(exists $Table{$key1}{$key2})) 
{ 
    #do whatever 
} 

C'è un modo più semplice e più pulita per farlo?

+0

Modifica la domanda per utilizzare i blocchi di codice per il codice. – justkt

risposta

8

Non è necessario controllare ogni livello dell'eradarchia: si può semplicemente andare per il valore che ti interessa. exists non verifica la definizione, solo se lo slot dell'hash esiste (potrebbe esistere con un valore non definito), quindi se si cura che il valore sia definito, è necessario chiamare defined anziché esistente. Se un valore non è definito, si valuta in contesto booleano false, in modo da poter digitare un po 'meno e ridurre il vostro esempio a:

if ($Table{$key1}{$key2}) 
{ 
    # do whatever 
} 

Tuttavia, se il valore di quella chiave è definito, ma è "falsa" (valuta numerico a zero, o è una stringa vuota), questo può causare un falso negativo, quindi dovremmo esplicitamente controllare definedness se questa è una possibilità:

if (defined $Table{$key1}{$key2}) 
{ 
    # do whatever 
} 

Se non si desidera autovivify $Table{$key1}, è possibile verificare la sua esistenza prima, che ci porta al modo "migliore" per il caso generale:

if (exists $Table{$key1} and defined $Table{$key1}{$key2}) 
{ 
    # do whatever 
} 

Se avete intenzione di fare questo molto per vari campi in un hash, si consiglia di aggiungere alcuni metodi di accesso OO stile che fare questo lavoro per voi:

sub has_field 
{ 
    my ($this, $fieldName) = @_; 
    return exists $this->{data} && defined $this->{data}{$fieldName}); 
} 

sono sicuro che avete letto già, ma non può far male a leggere di nuovo la relativa documentazione:

Data un'espressione che specifica un elemento di un hash o elemento di matrice, exists restituisce vero se l'elemento specificato nella hash o array è mai stato inizializzato, anche se il valore corrispondente è non definito. L'elemento non viene autovivificato se non esiste.
...
Un elemento di hash o di matrice può essere vero solo se è definito e definito se esiste, ma il contrario non è necessariamente valido.

+2

questo sarà sempre autovivifiy '$ Table {$ key1}' se non esiste –

+0

@Eric: vedere l'ultima modifica :) – Ether

+1

Farei risaltare il codice sbagliato inserendo ogni sorta di commenti intorno dicendo "Don fare questo !!!!!!! " –

5

Quello che segue è più corto e proteggerà da autovivifcation:

if (exists $table{$key1} and defined $table{$key1}{$key2}) {...} 

Gli altri controlli nel codice non sono necessari.

1

Controllare prima l'esistenza, quindi la definizione. (Un valore può esistere senza essere definito ma non definito senza esistere.) È necessario testare i livelli intermedi con exists per impedire l'autovivificazione involontaria. Per l'ultimo livello è sufficiente chiamare lo defined.Quando non ci sono troppi strati è facile da codificare direttamente:

if (exists $hash{a} && defined $hash{a}{b}) {...} 

Questo diventa imbarazzante se ci sono molti strati:

if (exists $hash{a} && exists $hash{a}{b} && exists $hash{a}{b}{c} ...) {...} 

In tal caso, è possibile scrivere una versione di defined che doesn 't autovivify valori intermedi:

sub safe_defined { 
    my $h = shift; 

    foreach my $k (@_) { 
     if (ref $h eq ref {}) { 
      return unless exists $h->{$k}; 
      $h = $h->{$k}; 
     } 
     else { 
      return; 
     } 
    } 

    return defined $h; 
} 

si utilizza in questo modo:

if (safe_defined(\%hash, qw(a b c))) { 
    say $hash{a}{b}{c}; 
} 

Nota: questa versione della funzione è limitata.

  • Gestisce solo gli hash annidati. Perl consente di costruire strutture arbitrarie di dati , come un hash di matrici di riferimenti scalari ...
  • Non supporta i riferimenti benedetti (cioè gli oggetti).

Una versione veramente generica è lasciata come esercizio per il lettore. ;)

1

È possibile controllare Data::Diver. Si tuffa in strutture di dati senza autovivifying. La sintassi sarebbe:

if (defined Dive(\%Table, $key1, $key2)) { ... } 

o anche:

if (defined(my $value = Dive(\%Table, $key1, $key2))) { 
    ...do something with $value... 
} 
0

Grande! Grazie a tutti per la risposta.

Dal momento che l'autovivifying è un problema per me, attualmente sto usando l'approccio "imbarazzante", vale a dire if (esiste $ tabella {$ chiave1} & & definito $ tabella {$ chiave1} {$ key2}) {

Fate quello

}

funziona per me, tuttavia, come voi ragazzi ha detto, ho 3-4 profondo livello di hash nidificato, il codice è po 'di disordine.

Controllerò Data: Diver. Quello sembra più bello.

Grazie, ancora,

Problemi correlati