To znacznie lepiej użyć czegoś podobnego modułu autovivification, aby wyłączyć tę funkcję, lub użyć Data::Diver. Jest to jednak jedno z prostych zadań, które mógłbym oczekiwać od programisty. Nawet jeśli nie użyjesz tej techniki tutaj, powinieneś znać ją dla innych problemów. Zasadniczo robi to Data::Diver
po zdjęciu interfejsu.
Jest to łatwe, gdy masz ochotę przejść przez strukturę danych (jeśli nie chcesz używać modułu, który robi to za Ciebie). W moim przykładzie tworzę podprogram check_hash
, który pobiera odwołanie do tablicy i odwołanie do tablicy kluczy do sprawdzenia. Sprawdza jeden poziom na raz. Jeśli klucza tam nie ma, nic nie zwraca. Jeśli klucz znajduje się w tym miejscu, przycina on skrót do tej części ścieżki i próbuje ponownie za pomocą następnego klawisza. Sztuczka polega na tym, że $hash
jest zawsze następną częścią drzewa do sprawdzenia. Umieszczam exists
w eval
na wypadek, gdyby następny poziom nie był referencją skrótu. Sztuczka nie kończy się niepowodzeniem, jeśli wartość skrótu na końcu ścieżki jest jakąś fałszywą wartością. Oto najważniejsza część zadania:
sub check_hash {
my($hash, $keys) = @_;
return unless @$keys;
foreach my $key (@$keys) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return 1;
}
Nie bój się całego kodu w następnym fragmencie. Ważną częścią jest podprogram check_hash
. Wszystko inne jest testowanie i demonstracji:
#!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';
}
Oto wyjście (minus zrzut danych):
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
Teraz możesz mieć jakąś inną kontrolę zamiast exists
. Być może chcesz sprawdzić, czy wartość na wybranej ścieżce jest prawdą, lub ciągiem znaków, lub innym hashowaniem, lub cokolwiek innego. To tylko kwestia dostarczenia właściwego czeku po sprawdzeniu, czy ścieżka istnieje. W tym przykładzie przekazuję podprocedurę referencyjną, która sprawdzi wartość, z którą zrezygnowałem.Mogę sprawdzić cokolwiek lubię:
#!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';
}
a jego produkcja:
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
Jestem zdumiony, że nie jest w perlfaq, biorąc pod uwagę, że to więcej niż większość FA QS już tam . Daj mi kilka minut, a ja to naprawię :) –
Oh look, tam to jest w perlfaq4: [Jak mogę sprawdzić, czy klucz istnieje w wielopoziomowym haszyszu?] (Http://faq.perl.org/ perlfaq4.html # How_can_I_check_if_a). Jest to w zasadzie podsumowanie tego wątku. Dzięki StackOverflow :) –