只有在Perl中存在哈希条目时才能有效地获取哈希条目

时间:2014-07-25 06:01:03

标签: perl hashtable exists autovivification

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

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是否足够聪明以优化此类案例?或者是否有其他习惯用来获取散列值而无需自动生成条目或进行两次连续查找?

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

$h->{$key}
如果密钥不存在,

将返回undef - 这是否意味着:

$h->{$key1}{$key2}
如果$ key1不存在,

会死,理由是我试图取消引用undef?如果是这样,为了避免这种情况,你可能仍然需要进行多级测试才能存在。

2 个答案:

答案 0 :(得分:2)

我不担心优化,因为哈希查找速度很快。但是对于你的第一个案例,你可以这样做:

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

类似地:

if ( ($v = $hash{key1}) && ($v = $v->{key2}) ) { 
    print "Got $v\n";
}

答案 1 :(得分:1)

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

my $value = $hash{$key};

对于多级访问,中间条目将自动进行。 e.g。

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!

我主要使用它作为一种在处理复杂数据结构时检测编程错误的方法。它对于检测错误键入的键名或无意中修改值非常有用。