perl - 如何检查 key 是否存在于深层 Perl 哈希中?

标签 perl hash autovivification

如果我 understand correctly , 调用if (exists $ref->{A}->{B}->{$key}) { ... }将出现$ref->{A}$ref->{A}->{B}即使它们在 if 之前不存在!

这似乎非常不受欢迎。那么我应该如何检查是否存在“深度”哈希键?

最佳答案

最好使用 autovivification 之类的东西。模块来关闭该功能,或使用 Data::Diver .然而,这是我希望程序员知道如何自己完成的简单任务之一。即使您在这里不使用此技术,您也应该知道它用于其他问题。这本质上是Data::Diver一旦你剥离它的界面,它就在做。

一旦您掌握了遍历数据结构的技巧(如果您不想使用为您完成它的模块),这很容易。在我的示例中,我创建了一个 check_hash子例程接受哈希引用和要检查的键的数组引用。它一次检查一个级别。如果 key 不存在,则不返回任何内容。如果 key 在那里,它会将散列修剪到路径的那一部分,并使用下一个 key 再次尝试。诀窍是$hash始终是要检查的树的下一部分。我把 existseval以防下一级不是哈希引用。如果路径末尾的哈希值是某种错误值,则诀窍是不会失败。这是任务的重要部分:

sub check_hash {
   my( $hash, $keys ) = @_;

   return unless @$keys;

   foreach my $key ( @$keys ) {
       return unless eval { exists $hash->{$key} };
       $hash = $hash->{$key};
       }

   return 1;
   }

不要被接下来的所有代码吓到。重要的部分只是 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';
   }

这是输出(减去数据转储):
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

现在,您可能想要进行其他检查,而不是 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';
    }

及其输出:
     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

关于perl - 如何检查 key 是否存在于深层 Perl 哈希中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3700069/

相关文章:

git - Perl -- commit-msg 钩子(Hook)在退出时不会停止提交 1

Perl HTML 解析器

hash - CRC32 整数散列到字符串

perl - 迭代时无意中将键添加到哈希

perl - 为什么在这种情况下 Perl 会自动激活?

arrays - 检测混合数组中的元素类型

perl - 与 perl 内置函数同名的包方法

java - 更新主键而不创建重复行?

hash - 密码盐如何帮助抵御彩虹表攻击?

java - map 的通用 autovivify 函数