2010-08-26 17 views
5

Supponendo che ho un hash come questo:Come scoprire se un hash Perl è multidimensionale?

$hash_ref = { 

    'hashes' => { 
    'h1' => { 'klf' => '1', 'moomooland' => '1' }, 
    'h2' => { 'klf' => '2', 'moomooland' => '2' }, 
    'h3' => { 'klf' => '3', 'moomooland' => '3' } 
    }, 

    'keys' => { 
    'k1' => 'key1', 
    'k2' => 'key2', 
    'k3' => 'key3' 
    } 

} 

Come potrei scoprire, il più semplice possibile, che hashes contiene altre 3 hash, mentre keys contiene coppie 3 chiave/valore?

ref restituirà HASH per entrambi e non sono sicuro se sia possibile scoprire la profondità di questi hash.

Grazie :)

+0

Sì, è possibile, ma mi chiedo perché vorresti saperlo. Questo può aiutare nel decidere una linea d'azione adatta. per esempio. Vuoi restituire la catena più profonda o la profondità di tutti i rami diversi? – Zaid

+2

Possibile duplicato: [Attraversando un hash multidimensionale in Perl] (http://stackoverflow.com/questions/160175/traversing-a-multi-dimensional-hash-in-perl) – Zaid

+0

Avrei bisogno di sapere questo così Posso invocare azioni diverse a seconda che si ottengano 3 hash o 3 coppie chiave/valore. Quindi, non ho necessariamente bisogno di conoscere la profondità esatta, se posso distinguere tra questi 2 in qualche altro modo. Vedo il tuo link ad altre domande ora, ma speravo che ciò potesse essere fatto senza ricorrere alla ricomposizione :) – sentinel

risposta

5

Se avete solo bisogno di sapere se un hash è multi-dimensionale, è possibile iterare i suoi valori e fermarsi se viene trovato un riferimento:

my $is_multi_dimensional = 0; 

for my $value (values %$hash_ref) { 
    if ('HASH' eq ref $value) { 
     $is_multi_dimensional = 1; 
     last; 
    } 
} 

oppure è possibile utilizzare la funzione each():

while (my (undef, $value) = each %$hash_ref) { 
    if ('HASH' eq ref $value) { 
     $is_multi_dimensional = 1; 
     last; 
    } 
} 
+0

Oh, questo è abbastanza brillante. Così semplice, eppure così logico ed efficace. Argh, perché non ci ho pensato? :) Grazie, Eugene, questo funziona perfettamente per quello di cui ho bisogno, anche se ora capisco che la formulazione della mia domanda non spiega veramente che questo è quello che stavo cercando, piuttosto che ottenere la profondità esatta/attuale di un hash. Ops. – sentinel

+1

Sei sicuro che each() è più efficiente di values ​​()? Dal momento che utilizza l'iteratore interno, potrebbe verificarsi un cortocircuito che aiuta gli hash giganti, ma presenta l'inconveniente generale di utilizzare l'iteratore (globale!) Dell'hash. each() è una disfunzione, IMO. – tsee

+2

@tsee: è più efficiente per gli hash più grandi, perché non crea un elenco intermedio di valori. Ed è l'unico modo per ottenere hash molto grandi (ad esempio legati). Naturalmente, si dovrebbe ricordare che esiste un singolo iteratore per ogni hash nel programma, in base alla progettazione. –

1

Si può provare questo per conoscere la profondità di Perl hash:

#!/usr/bin/perl 
use strict; 
my $hash_ref = { 

    'hashes' => { 
    'h1' => { 'klf' => '1', 'moomooland' => '1' }, 
    'h2' => { 'klf' => '2', 'moomooland' => '2' }, 
    'h3' => { 'klf' => '3', 'moomooland' => '3' } 
    }, 

    'keys' => { 
    'k1' => 'key1', 
    'k2' => 'key2', 
    'k3' => 'key3' 
    } 

}; 
print_nested_hash($hash_ref, 0); 

sub print_nested_hash { 
    my $hash_ref = shift; 
    my $depth = shift; 
    foreach my $key (sort keys %{$hash_ref}) { 
     print ' ' x $depth, $key, "\n"; 

     if (ref($hash_ref->{$key}) eq "HASH") { 
      print_nested_hash($hash_ref->{$key}, $depth+1); 
     } 
    } 
} 

USCITA:

hashes 
    h1 
     klf 
     moomooland 
    h2 
     klf 
     moomooland 
    h3 
     klf 
     moomooland 
keys 
    k1 
    k2 
    k3 
5

È inoltre possibile first e grep per questo:

use List::Util 'first'; 
say 'is multi-dimensional' if first { ref } values %$hash_ref; 

# explicitly check for HASH ref this time 
my $how_many_md_hashes = grep { 'HASH' eq ref } values %$hash_ref; 

NB. first (parte del modulo List::Util) cortocircuiti quindi è ideale per condizionali ed è probabilmente la più veloce di tutte le opzioni possibili.

/I3az/

+2

Puoi usare 'any' invece di 'first' se ti interessa solo la verità piuttosto che il valore reale che corrisponde: 'usa List :: MoreUtils 'any'; my $ is_multi_dimensional = qualsiasi {ref} valori% $ hash_ref; ' – Ether

Problemi correlati