2

我经常写这样的代码片段:

if (exists $myHash->{$key}) {
    $value = $myHash->{$key};
}

我想要做的是从哈希中获取值,如果哈希中有那个键,同时我想避免自动激活哈希条目,如果它不存在的话。

然而,这让我感到非常低效:我正在做一个哈希查找以找出一个键是否存在,然后如果它确实存在,我正在对同一个键进行另一个哈希查找以提取它。

它在多级结构中变得更加低效:

if (exists $myHash->{$key1} 
    && exists $myHash->{$key1}{$key2} 
    && exists $myHash->{$key1}{$key2}{$key3}) {

    $value = $myHash->{$key1}{$key2}{$key3};
}

在这里,我大概做了 9 次哈希查找而不是 3 次!

perl 是否足够聪明来优化这种情况?或者是否有其他一些习惯用法可以在不自动激活条目或进行两次连续查找的情况下获取哈希值?

我知道自动生存模块,但如果可能的话,我正在寻找不需要安装 XS 模块的解决方案。此外,我还没有机会尝试这个模块,我不完全确定在多级哈希的情况下会发生什么 - pod 说:

$h->{$key}

如果密钥不存在,将返回 undef - 这是否意味着:

$h->{$key1}{$key2}

如果 $key1 不存在,我会因为我试图取消引用 undef 而死吗?如果是这样,为了避免这种情况,您可能仍然需要进行多级测试以确保存在。

4

2 回答 2

2

我不会担心优化,因为哈希查找很快。但是对于您的第一种情况,您可以执行以下操作:

if (my $v = $hash{$key}) {
    print "have $key => $v\n";
}

相似地:

if ( ($v = $hash{key1}) && ($v = $v->{key2}) ) { 
    print "Got $v\n";
}
于 2014-07-25T06:08:31.587 回答
2

单级访问不会发生自动激活,因此您可以安全地编写

my $value = $hash{$key};

对于多级访问,中间条目将被自动激活。例如

my $value = $hash{a}{b};

$hash{a}如果不存在,将创建对空哈希的引用。(如果它确实存在并且不是哈希引用,perl 将抛出一个错误并死掉。)为避免这种情况,您需要先检查每个级别。您可以编写一个子例程来检查任意嵌套键的存在。

sub safe_exists {
    my $x = shift;
    foreach my $k (@_) {
        no warnings 'uninitialized';
        return unless ref $x eq ref {};
        return unless exists $x->{$k};
        $x = $x->{$k};
    }
    return 1;
}

if (safe_exists(\%hash, qw(a b))) {...}

根据您的算法(以及您尝试避免自动激活的原因),锁定您的哈希可能是no autovivification多层exists测试的有用替代方案。

use Hash::Util;

my %hash = (a => { b => 1 });
Hash::Util::lock_hash_recurse(%hash);

say $h{a}{b}; # 1
say $h{a}{c}; # error!

在处理复杂的数据结构时,我主要使用它来检测编程错误。它对于检测键入错误的键名或无意修改值很有用。

于 2014-07-25T14:02:11.270 回答