2010-12-02 14 views
1

En Perl, j'ai un hachage de hachages créé avec une boucle semblable au suivantPerl, vérifiez si la paire existe dans hachage de hachages

my %HoH 
    for my $i (1..10) { 
     $HoH{$a}{$b} = $i; 
    } 

$ a et $ b sont des variables qui ont une certaine valeur lorsque le HoH est rempli. Après avoir créé le HoH, comment puis-je vérifier si une paire particulière ($ c, $ d) existe dans le HoH? Ce qui suit ne fonctionne pas

if (defined $HoH{$c}{$d}) {...} 

parce que si c $ n'existe pas en HvH déjà, il sera créé une clé sans valeur.

+0

Pas tout à fait: Si le hachage ne contient pas de valeur pour '$ c', il sera créé en tant que référence à un hachage (nouveau, anonyme) parce que c'est ainsi qu'il est utilisé. C'est ce qu'on appelle "autovivification". –

Répondre

3
use Data::Dumper; 

my %HoH; 

$HoH{A}{B} = 1; 

if(exists $HoH{C} && exists $HoH{C}{D}) { 
    print "exists\n"; 
} 

print Dumper(\%HoH); 

if(exists $HoH{C}{D}) { 
    print "exists\n"; 
} 

print Dumper(\%HoH); 

Sortie:

$VAR1 = { 
      'A' => { 
        'B' => 1 
       } 
     }; 
$VAR1 = { 
      'A' => { 
        'B' => 1 
       }, 
      'C' => {} 
     }; 

L'autovivification entraîne la création des clés. "existe" dans mon deuxième exemple montre cela donc le premier exemple vérifie les deux clés individuellement.

+0

Vous n'avez pas vraiment besoin d'utiliser l'encombrant 'exists', vous savez. – tchrist

+0

Merci! Une idée de pourquoi le premier exemple fonctionne comme il se doit alors que le second ne fonctionne pas? –

+0

http://en.wikipedia.org/wiki/Autovivification –

0

Vous devez utiliser la fonction exists

existe EXPR

Étant donné une expression qui spécifie un élément d'un hachage, renvoie true si l'élément spécifié dans le hachage a jamais été initialisés , même si la valeur correspondante est indéfinie.

Notez que le EXPR peut être arbitrairement compliquée tant que l'opération finale est une recherche de clé de hachage ou d'un tableau ou le nom sous-programme:

  1. si (existe $ ref -> {A} -> {B} -> {$ key}) {}
  2. si (existe $ hash {A} {B} {$ key}) {}
+0

Ne fonctionne pas, je pense que parce que je n'essaie pas de voir si une clé $ existe, mais si $ hash {A} {B} est défini ... Merci quand même! –

+2

'exists' ne subvertit pas l'autovivification. –

+0

'existe' n'est ni une mesure nécessaire ni suffisante pour effectuer la solution ici. – tchrist

4

Écrire

if (defined $HoH{$c}{$d}) {...} 

« fonctionnera » d'autant que cela vous dira si oui ou non $HoH{$c}{$d} a une valeur définie. Le problème est que si $HoH{$c} n'existe pas, il sera créé (avec une valeur appropriée) afin que $HoH{$c}{$d} puisse être testé. Ce processus est appelé "autovivification". C'est pratique lors de la définition de valeurs, par ex.

my %hoh; 
$hoh{a}{b} = 1; # Don't need to set '$hoh{a} = {}' first 

mais peu pratique lors de la récupération des valeurs éventuellement inexistantes. Je souhaite que Perl ait été assez intelligent pour effectuer seulement l'autovivification pour les expressions utilisées comme lvalues ​​et court-circuiter pour retourner undef pour les rvalues ​​mais, hélas, ce n'est pas si magique. Le pragma autovivification (disponible sur CPAN) ajoute la fonctionnalité pour cela.

Pour éviter autovivification vous devez tester les valeurs intermédiaires d'abord:

if (exists $HoH{$c} && defined $HoH{$c}{$d}) { 
    ... 
} 
+0

Qu'est-ce qui est avec 'exists'? – tchrist

+1

@tchrist: Je pense qu'il exprime plus clairement l'intention de "Y at-il quelque chose là-bas?" Sans le 'existe 'cela ressemble à un test pour la vérité booléenne. Les références sont vraies bien sûr, donc ça marche et c'est plus concis, mais ce n'est pas vraiment la vérité de la valeur qui nous intéresse. Comme un avantage secondaire, il est légèrement plus rapide de vérifier l'existence que de récupérer la valeur. –

+1

Peut-être. Je pense que vous et moi pensons simplement à ces choses de différentes façons. Dans mon modèle, le 'existe', étant redondant, attire une attention particulière sur lui-même et me fait réfléchir s'il n'y a pas quelque chose de sournois à l'horizon. J'ai la même réaction quand je vois quelqu'un écrire '' $ var ''quand' $ var' le ferait. Cela dit, j'écris toujours '$ count = scalar (@array)', même si le 'scalar' est superflu. Je pense que nous souhaitons nous rappeler à nous-mêmes et à d'autres de notre retrait, mais que ce qui "rappelle" les besoins peut varier entre l'auteur et le lecteur. – tchrist

2

Plusieurs façons:

if ($HoH{$c} && defined $HoH{$c}{$d}) {...} 

ou

if (defined ${ $HoH{$c} || {} }{$d}) {...} 

ou

no autovivification; 
if (defined $HoH{$c}{$d}) {...} 

ou

use Data::Diver; 
if (defined Data::Diver::Dive(\%HoH, $c, $d)) {...} 
+1

Je trouve la deuxième option offensante pour mes délicates sensibilités. (c'est-à-dire que c'est une astuce intéressante, mais s'il-vous-plaît ne l'utilisez pas.) –

+0

Qu'est-ce qui est avec toutes les activités 'définies'? – tchrist

+0

@Michael Carman: c'est un idiome. J'ai travaillé sur un code de base où il était omniprésent et une fois que je l'ai adapté, il ne m'a plus offensé. Je soupçonne que la même chose est vraie de la plupart des idiomes: ils sont mieux utilisés largement ou pas du tout. – ysth

0

Mon avis:

use List::Util qw<first>; 
use Params::Util qw<_HASH>; 

sub exists_deep (\[%$]@) { 
    my $ref = shift; 
    return unless my $h = _HASH($ref) // _HASH($$ref) 
       and defined(my $last_key = pop) 
       ; 
    # Note that this *must* be a hash ref, for anything else to make sense. 
    return if first { !($h = _HASH($h->{ $_ })) } @_; 
    return exists $h->{ $last_key }; 
} 

Vous pouvez aussi le faire récursive. Vous pouvez également créer une structure de descente permettant un arrayref intermédiaire et même terminal avec juste un peu de codage supplémentaire.