【问题标题】:Perl, check if pair exists in hash of hashesPerl,检查哈希哈希中是否存在对
【发布时间】:2011-05-19 04:55:10
【问题描述】:

在 Perl 中,我使用类似于以下的循环创建了一个哈希值

    my %HoH
    for my $i (1..10) {
      $HoH{$a}{$b} = $i; 
    }

$a 和 $b 是在填充 HoH 时确实具有某些值的变量。创建 HoH 后,如何检查特定对 ($c, $d) 是否存在于 HoH 中?以下不起作用

if (defined $HoH{$c}{$d}) {...}

因为如果 $c 在 HoH 中已经不存在,它将被创建为没有值的键。

【问题讨论】:

  • 不完全:如果散列不包含 $c 的值,它将被创建为对(新的、匿名的)散列的引用,因为这就是它的使用方式。这称为“自动复活”。

标签: perl hash


【解决方案1】:

写作

if (defined $HoH{$c}{$d}) {...}

将“起作用”,因为它会告诉您$HoH{$c}{$d} 是否具有定义的值。问题是如果$HoH{$c} 尚不存在,它将被创建(具有适当的值),以便可以测试$HoH{$c}{$d}。这个过程被称为“自动存活”。设置值时很方便,例如

my %hoh;
$hoh{a}{b} = 1; # Don't need to set '$hoh{a} = {}' first

但在检索可能不存在的值时很不方便。我希望 Perl 足够聪明,只对用作左值的表达式执行自动激活,并为右值短路返回 undef,但是,唉,它并没有那么神奇。 autovivification pragma(在 CPAN 上可用)添加了执行此操作的功能。

为避免自动激活,您需要先测试中间值:

if (exists $HoH{$c} && defined $HoH{$c}{$d}) {
     ...
}

【讨论】:

  • @tchrist:我认为它更清楚地表达了“那里有东西吗?”的意图。没有exists,它看起来像是对布尔真值的测试。引用当然是正确的,所以它有效并且更简洁,但这并不是我们真正关心的价值的真实性。附带的好处是,检查是否存在比检索值要快一些。
  • 也许吧。我怀疑你和我只是以不同的方式思考这些事情。在我的模型中,exists 是多余的,引起了特别的注意,让我思考是否有什么鬼鬼祟祟的东西。当我看到有人写"$var"$var 会这样做时,我也有同样的反应。也就是说,我自己一直写$count = scalar(@array),尽管scalar 是多余的。我想我们希望提醒自己和他人我们的缩进,但是“需要”提醒的内容可能因作者和读者而异。
  • 如果你真的在测试存在,你必须小心不要离开exists。如果你想知道%foo中是否存在bar,而只做if $foo{bar}$foo{bar} == 0,可能得不到你想要的结果。
【解决方案2】:
use Data::Dumper;

my %HoH;

$HoH{A}{B} = 1;

if(exists $HoH{C} && exists $HoH{C}{D}) {
   print "exists\n";
}

print Dumper(\%HoH);

if(exists $HoH{C}{D}) {
   print "exists\n";
}

print Dumper(\%HoH);

输出:

$VAR1 = {
          'A' => {
                   'B' => 1
                 }
        };
$VAR1 = {
          'A' => {
                   'B' => 1
                 },
          'C' => {}
        };

自动激活导致密钥被创建。我的第二个示例中的“存在”显示了这一点,因此第一个示例分别检查两个键。

【讨论】:

  • 你真的不必使用繁琐的exists,你知道的。
  • 谢谢!知道为什么第一个示例可以正常工作而第二个示例不能正常工作吗?
【解决方案3】:

几种方式:

if ( $HoH{$c} && defined $HoH{$c}{$d} ) {...}

if ( defined ${ $HoH{$c} || {} }{$d} ) {...}

no autovivification;
if (defined $HoH{$c}{$d}) {...}

use Data::Diver;
if ( defined Data::Diver::Dive( \%HoH, $c, $d ) ) {...}

【讨论】:

  • 我发现第二个选项冒犯了我细腻的情感。 (即这是一个有趣的技巧,但请不要使用它。)
  • defined 的所有业务是怎么回事?
  • @Michael Carman:这是一个成语。我在一个无处不在的代码库上工作,一旦我适应了它就不再冒犯我了。我怀疑大多数习语也是如此:它们最好被广泛使用或根本不使用。
  • @tchrist:这就是 OP 正在检查的内容。可能不是他想要的,但那是另一回事。
【解决方案4】:

你必须使用exists函数

存在 EXPR

给定一个表达式,指定一个 哈希的元素,如果 哈希中的指定元素曾经 已初始化,即使 对应的值未定义。

注意EXPR可以任意 复杂只要最后 操作是哈希或数组键 查找或子程序名称:

  1. 如果(存在 $ref->{A}->{B}->{$key}){ }
  2. 如果(存在 $hash{A}{B}{$key}){}

【讨论】:

  • 不起作用,我想是因为我不是想查看 $key 是否存在,而是 $hash{A}{B} 是否已定义......无论如何,谢谢!
  • exists 不会破坏自动存活。
  • exists 既不是实现此处解决方案的必要措施,也不是充分措施。
【解决方案5】:

我的看法:

use List::Util   qw<first>;
use Params::Util qw<_HASH>;

sub exists_deep (\[%$]@) {
    my $ref = shift;
    return unless my $h = _HASH( $ref ) // _HASH( $$ref )
              and defined( my $last_key = pop )
              ;
    # Note that this *must* be a hash ref, for anything else to make sense.
    return if first { !( $h = _HASH( $h->{ $_ } )) } @_;
    return exists $h->{ $last_key };
}

您也可以递归地执行此操作。您还可以创建一个下降结构,允许中间甚至终端数组引用,只需一点额外的编码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-28
    • 1970-01-01
    • 2012-08-10
    • 1970-01-01
    • 2011-12-28
    • 2017-04-13
    • 1970-01-01
    相关资源
    最近更新 更多