Es ist viel besser so etwas wie das autovivification Modul zu verwenden, die Funktion zu deaktivieren, oder Data::Diver zu verwenden. Dies ist jedoch eine der einfachen Aufgaben, von denen ich erwarte, dass ein Programmierer es selbstständig macht. Auch wenn Sie diese Technik hier nicht anwenden, sollten Sie sie für andere Probleme kennen. Dies ist im Wesentlichen was Data::Diver
tut, sobald Sie seine Schnittstelle entfernen.
Dies ist einfach, sobald Sie den Trick des Gehens einer Datenstruktur erhalten (wenn Sie kein Modul verwenden möchten, das es für Sie tut). In meinem Beispiel erstelle ich eine Subroutine check_hash
, die eine Hash-Referenz und eine Array-Referenz der zu überprüfenden Schlüssel verwendet. Es prüft jeweils eine Ebene. Wenn der Schlüssel nicht vorhanden ist, gibt er nichts zurück. Wenn der Schlüssel dort ist, wird der Hash auf genau diesen Teil des Pfades zurückgesetzt und es wird erneut mit dem nächsten Schlüssel versucht. Der Trick ist, dass $hash
immer der nächste Teil des zu überprüfenden Baumes ist. Ich setze die exists
in eine eval
für den Fall, dass die nächste Ebene keine Hash-Referenz ist. Der Trick besteht darin, nicht zu versagen, wenn der Hash-Wert am Ende des Pfades eine Art falscher Wert ist. Hier ist der wichtige Teil der Aufgabe:
sub check_hash {
my($hash, $keys) = @_;
return unless @$keys;
foreach my $key (@$keys) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return 1;
}
Lassen Sie sich nicht durch den ganzen Code im nächsten Bit erschrecken. Der wichtige Teil ist nur das check_hash
Unterprogramm. Alles andere ist die Erprobung und Demonstration:
#!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';
}
Hier ist der Ausgang (abzüglich der Daten-Dump):
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
Jetzt könnten Sie eine andere Kontrolle statt exists
haben wollen. Vielleicht möchten Sie überprüfen, ob der Wert im ausgewählten Pfad wahr ist, oder eine Zeichenfolge oder eine andere Hash-Referenz oder was auch immer. Das ist nur eine Frage des richtigen Checks, sobald Sie verifiziert haben, dass der Pfad existiert. In diesem Beispiel übergebe ich eine Subroutinenreferenz, die den Wert prüft, mit dem ich aufgehört habe.Ich kann für alles überprüfen Ich mag:
#!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';
}
und seine Ausgabe:
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
Ich bin erstaunt, dass dies nicht in der perlfaq ist, wenn man bedenkt es mehr FA ist als die meisten der Qs bereits dort in . Geben Sie mir ein paar Minuten und ich werde das beheben :) –
Oh schau, da ist es in Perlfaq4: [Wie kann ich überprüfen, ob ein Schlüssel in einem Multilevel-Hash existiert?] (Http://faq.perl.org/ perlfaq4.html # How_can_I_check_if_a). Es ist im Wesentlichen eine Zusammenfassung dieses Threads. Danke StackOverflow :) –