Dav*_*d B 28 perl hash autovivification
如果我理解正确,呼召if (exists $ref->{A}->{B}->{$key}) { ... }
将会存在$ref->{A}
,$ref->{A}->{B}
即使它们之前不存在if
!
这似乎非常不受欢迎.那么我该如何检查是否存在"深度"哈希键?
bri*_*foy 38
使用像autofivification模块这样的功能来关闭该功能,或使用Data :: Diver会好得多.然而,这是我希望程序员自己知道如何做的简单任务之一.即使你不在这里使用这种技术,你应该知道其他问题.Data::Diver
一旦剥离其界面,这基本上就是在做什么.
一旦你获得了走数据结构的技巧(如果你不想使用为你做这个的模块),这很容易.在我的示例中,我创建了一个check_hash
子例程,它接受哈希引用和要检查的键的数组引用.它一次检查一个级别.如果密钥不存在,则不返回任何内容.如果密钥在那里,它会将哈希修剪到路径的那一部分,然后再次使用下一个密钥.诀窍在于,$hash
它始终是要检查的树的下一部分.我把它exists
放在一个eval
万一以防下一级不是哈希引用.如果路径末尾的哈希值是某种假值,那么技巧就不会失败.这是任务的重要部分:
sub check_hash {
my( $hash, $keys ) = @_;
return unless @$keys;
foreach my $key ( @$keys ) {
return unless eval { exists $hash->{$key} };
$hash = $hash->{$key};
}
return 1;
}
Run Code Online (Sandbox Code Playgroud)
不要被下一位的所有代码吓到.重要的部分只是check_hash
子程序.其他一切都是测试和演示:
#!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';
}
Run Code Online (Sandbox Code Playgroud)
这是输出(减去数据转储):
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
Run Code Online (Sandbox Code Playgroud)
现在,您可能希望进行其他检查而不是exists
.也许你想检查所选路径的值是否为真,或者是字符串,还是其他哈希引用,或者其他什么.这只是在您确认路径存在后提供正确检查的问题.在这个例子中,我传递一个子程序引用,它将检查我留下的值.我可以检查我喜欢的任何东西:
#!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';
}
Run Code Online (Sandbox Code Playgroud)
它的输出:
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
Run Code Online (Sandbox Code Playgroud)
pha*_*lon 15
您可以使用autovivification pragma来停用引用的自动创建:
use strict;
use warnings;
no autovivification;
my %foo;
print "yes\n" if exists $foo{bar}{baz}{quux};
print join ', ', keys %foo;
Run Code Online (Sandbox Code Playgroud)
它也是词法,意味着它只会在你指定的范围内停用它.
exist
在查看顶层之前检查每个级别.
if (exists $ref->{A} and exists $ref->{A}{B} and exists $ref->{A}{B}{$key}) {
}
Run Code Online (Sandbox Code Playgroud)
如果您觉得烦人,可以随时查看CPAN.例如,有Hash::NoVivify
.
看看Data :: Diver.例如:
use Data::Diver qw(Dive);
my $ref = { A => { foo => "bar" } };
my $value1 = Dive($ref, qw(A B), $key);
my $value2 = Dive($ref, qw(A foo));
Run Code Online (Sandbox Code Playgroud)