Il est préférable d'utiliser quelque chose comme le module autovivification pour désactiver cette fonction, ou d'utiliser Data::Diver. Cependant, c'est une des tâches simples que je m'attendrais à ce qu'un programmeur sache faire seul. Même si vous n'utilisez pas cette technique ici, vous devriez le savoir pour d'autres problèmes. C'est essentiellement ce que fait Data::Diver
une fois que vous avez retiré son interface.
Ceci est facile une fois que vous avez l'astuce de marcher une structure de données (si vous ne voulez pas utiliser un module qui le fait pour vous). Dans mon exemple, je crée un sous-programme check_hash
qui prend une référence de hachage et une référence de tableau de clés à vérifier. Il vérifie un niveau à la fois. Si la clé n'est pas là, elle ne retourne rien. Si la clé est là, elle élague le hachage juste à cette partie du chemin et essaie à nouveau avec la clé suivante. L'astuce est que $hash
est toujours la prochaine partie de l'arbre à vérifier. Je mets le exists
dans un eval
dans le cas où le niveau suivant n'est pas une référence de hachage. L'astuce est de ne pas échouer si la valeur de hachage à la fin du chemin est une sorte de fausse valeur. Voici la partie importante de la tâche:
sub check_hash {
my($hash, $keys) = @_;
return unless @$keys;
foreach my $key (@$keys) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return 1;
}
Ne soyez pas effrayé par tout le code dans le bit suivant. La partie importante est juste le sous-programme check_hash
. Tout le reste est le test et de démonstration
#!perl
use strict;
use warnings;
use 5.010;
sub check_hash {
my($hash, $keys) = @_;
return unless @$keys;
foreach my $key (@$keys) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return 1;
}
my %hash = (
a => {
b => {
c => {
d => {
e => {
f => 'foo!',
},
f => 'foo!',
},
},
f => 'foo!',
g => 'goo!',
h => 0,
},
f => [ qw(foo goo moo) ],
g => undef,
},
f => sub { 'foo!' },
);
my @paths = (
[ qw(a b c d ) ], # true
[ qw(a b c d e f) ], # true
[ qw(b c d) ], # false
[ qw(f b c) ], # false
[ qw(a f) ], # true
[ qw(a f g) ], # false
[ qw(a g) ], # true
[ qw(a b h) ], # false
[ qw(a) ], # true
[ qw() ], # false
);
say Dumper(\%hash); use Data::Dumper; # just to remember the structure
foreach my $path (@paths) {
printf "%-12s --> %s\n",
join(".", @$path),
check_hash(\%hash, $path) ? 'true' : 'false';
}
Voici la sortie (moins la décharge de données):
a.b.c.d --> true
a.b.c.d.e.f --> true
b.c.d --> false
f.b.c --> false
a.f --> true
a.f.g --> false
a.g --> true
a.b.h --> true
a --> true
--> false
Maintenant, vous voudrez peut-être avoir un autre chèque au lieu de exists
. Peut-être que vous voulez vérifier que la valeur du chemin choisi est vrai, ou une chaîne, ou une autre référence de hachage, ou peu importe. C'est juste une question de fournir la bonne vérification une fois que vous avez vérifié que le chemin existe. Dans cet exemple, je passe une référence de sous-programme qui va vérifier la valeur que j'ai laissée avec.Je peux vérifier tout ce que j'aime:
#!perl
use strict;
use warnings;
use 5.010;
sub check_hash {
my($hash, $sub, $keys) = @_;
return unless @$keys;
foreach my $key (@$keys) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return $sub->($hash);
}
my %hash = (
a => {
b => {
c => {
d => {
e => {
f => 'foo!',
},
f => 'foo!',
},
},
f => 'foo!',
g => 'goo!',
h => 0,
},
f => [ qw(foo goo moo) ],
g => undef,
},
f => sub { 'foo!' },
);
my %subs = (
hash_ref => sub { ref $_[0] eq ref {} },
array_ref => sub { ref $_[0] eq ref [] },
true => sub { ! ref $_[0] && $_[0] },
false => sub { ! ref $_[0] && ! $_[0] },
exist => sub { 1 },
foo => sub { $_[0] eq 'foo!' },
'undef' => sub { ! defined $_[0] },
);
my @paths = (
[ exist => qw(a b c d ) ], # true
[ hash_ref => qw(a b c d ) ], # true
[ foo => qw(a b c d ) ], # false
[ foo => qw(a b c d e f) ], # true
[ exist => qw(b c d) ], # false
[ exist => qw(f b c) ], # false
[ array_ref => qw(a f) ], # true
[ exist => qw(a f g) ], # false
[ 'undef' => qw(a g) ], # true
[ exist => qw(a b h) ], # false
[ hash_ref => qw(a) ], # true
[ exist => qw() ], # false
);
say Dumper(\%hash); use Data::Dumper; # just to remember the structure
foreach my $path (@paths) {
my $sub_name = shift @$path;
my $sub = $subs{$sub_name};
printf "%10s --> %-12s --> %s\n",
$sub_name,
join(".", @$path),
check_hash(\%hash, $sub, $path) ? 'true' : 'false';
}
et sa sortie:
exist --> a.b.c.d --> true
hash_ref --> a.b.c.d --> true
foo --> a.b.c.d --> false
foo --> a.b.c.d.e.f --> true
exist --> b.c.d --> false
exist --> f.b.c --> false
array_ref --> a.f --> true
exist --> a.f.g --> false
undef --> a.g --> true
exist --> a.b.h --> true
hash_ref --> a --> true
exist --> --> false
Je suis étonné que ce n'est pas dans le perlfaq, étant donné qu'il est plus FA que la plupart des Qs déjà là . Donnez-moi quelques minutes et je corrigerai cela :) –
Oh regarde, là c'est dans perlfaq4: [Comment puis-je vérifier si une clé existe dans un hash multiniveau?] (Http://faq.perl.org/ perlfaq4.html # How_can_I_check_if_a). C'est essentiellement un résumé de ce fil. Merci StackOverflow :) –