Tốt hơn bạn nên sử dụng mô-đun autovivification để tắt tính năng đó hoặc sử dụng Data::Diver. Tuy nhiên, đây là một trong những nhiệm vụ đơn giản mà tôi mong đợi một lập trình viên biết cách tự làm. Ngay cả khi bạn không sử dụng kỹ thuật này ở đây, bạn nên biết nó cho các vấn đề khác. Đây thực chất là những gì mà Data::Diver
đang thực hiện khi bạn bỏ qua giao diện của nó.
Điều này rất dễ dàng khi bạn nhận được mẹo để đi bộ một cấu trúc dữ liệu (nếu bạn không muốn sử dụng mô-đun thực hiện điều đó cho bạn). Trong ví dụ của tôi, tôi tạo một chương trình con check_hash
lấy tham chiếu băm và tham chiếu mảng của các phím để kiểm tra. Nó kiểm tra một cấp tại một thời điểm. Nếu khóa không có ở đó, nó sẽ không trả về gì cả. Nếu khóa ở đó, nó sẽ băm băm chỉ là một phần của đường dẫn và thử lại bằng phím tiếp theo. Bí quyết là $hash
luôn là phần tiếp theo của cây để kiểm tra. Tôi đặt exists
trong một số eval
trong trường hợp cấp tiếp theo không phải là tham chiếu băm. Bí quyết không phải là thất bại nếu giá trị băm ở cuối đường dẫn là một loại giá trị sai nào đó. Đây là một phần quan trọng của tác vụ:
sub check_hash {
my($hash, $keys) = @_;
return unless @$keys;
foreach my $key (@$keys) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return 1;
}
Đừng sợ bởi tất cả mã trong bit tiếp theo. Phần quan trọng chỉ là chương trình con check_hash
. Mọi thứ khác là thử nghiệm và trình diễn:
#!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';
}
Dưới đây là đầu ra (trừ các bãi chứa dữ liệu):
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
Bây giờ, bạn có thể muốn có một số kiểm tra khác thay vì exists
. Có thể bạn muốn kiểm tra xem giá trị tại đường dẫn đã chọn có đúng hay là một chuỗi hoặc một tham chiếu băm khác hay bất kỳ thứ gì. Đó chỉ là vấn đề cung cấp kiểm tra đúng khi bạn đã xác minh rằng đường dẫn tồn tại. Trong ví dụ này, tôi chuyển một tham chiếu chương trình con sẽ kiểm tra giá trị mà tôi đã bỏ đi.Tôi có thể kiểm tra bất cứ điều gì tôi thích:
#!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';
}
Và sản lượng của nó:
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
Tôi ngạc nhiên rằng đây không phải là trong perlfaq, coi đó là hơn FA hơn hầu hết các Q đã có trong đó . Hãy cho tôi một vài phút và tôi sẽ sửa chữa điều đó :) –
Oh, có trong perlfaq4: [Làm thế nào tôi có thể kiểm tra xem một khóa tồn tại trong một băm đa cấp?] (Http://faq.perl.org/ perlfaq4.html # How_can_I_check_if_a). Đó là bản tóm tắt về chủ đề này. Cảm ơn StackOverflow :) –