5

我有时会访问这样的哈希:

if(exists $ids{$name}){
    $id = $ids{$name};
}

这是好习惯吗?我有点担心它包含两个真正应该完成的查找。有没有更好的方法来检查存在并赋值?

4

6 回答 6

10

通过检查exists,您可以防止自动存活。请参阅Autovivification :它是什么,我为什么要关心?.

更新:正如Trendels在下面指出的那样,在您发布的示例中,自动复活不会发挥作用。我假设实际代码涉及多级哈希。

这是一个插图:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my (%hash, $x);

if ( exists $hash{test}->{vivify} ) {
    $x = $hash{test}->{vivify}->{now};
}

print Dumper \%hash;

$x = $hash{test}->{vivify}->{now};

print Dumper \%hash;

__END__


C:\Temp> t
$VAR1 = {
    'test' => {}
};
$VAR1 = {
    'test' => {
        'vivify' => {}
    }
};
于 2009-06-10T18:42:19.183 回答
2

您可以将 apply Hash::Util的 lock_keys 用于哈希。然后在评估中执行你的任务。

#!/usr/bin/perl
use Hash::Util qw/lock_keys/;

my %a = (
    1 => 'one',
    2 => 'two'
);

lock_keys(%a);

eval {$val = $a{2}};     # this assignment completes
eval {$val = $a{3}};     # this assignment aborts
print "val=$val\n";      # has value 'two'
于 2009-06-11T02:02:55.453 回答
1

您可以通过这样的查找来做到这一点:

$tmp = $ids{$name};
$id = $tmp if (defined $tmp);

但是,除非我看到这是一个瓶颈,否则我不会打扰

于 2009-06-10T18:34:09.247 回答
1

在这种情况下,性能并不重要,请参阅“Devel::NYTProf”。但要回答你的问题:

如果散列中的值不存在,“存在”非常快

if(exists $ids{$name}){
    $id = $ids{$name};
}

但如果它确实存在,则完成第二次查找。如果该值可能存在,那么只查找一次会更快

$id = $ids{$name};
if($id){
    #....
}

从 perl 邮件列表中查看这个小基准。

#!/usr/bin/perl -w
use strict;
use Benchmark qw( timethese );

use vars qw( %hash );
@hash{ 'A' .. 'Z', 'a' .. 'z' } = (1) x 52;

my $key = 'xx';
timethese 10000000, {
        'defined' => sub {
                if (defined $hash{$key}) { my $x = $hash{$key}; return $x; };
                return 0;
        },
        'defined_smart' => sub {
                my $x = $hash{$key};
                if (defined $x) {
                        return $x;
                };
                return 0;
        },
        'exists' => sub {
                if (exists $hash{$key}) { my $x = $hash{$key}; return $x; };
                return 0;
        },
        'as is' => sub {
                if ($hash{$key}) { my $x = $hash{$key}; return $x; };
                return 0;
        },
        'as is_smart' => sub {
                my $x = $hash{$key};
                if ($x) { return $x; };
                return 0;
        },

};

使用不存在的 key('xx') 表明 'exists' 是赢家。

Benchmark: timing 10000000 iterations of as is, as is_smart, defined, defined_smart, exists...
     as is:  1 wallclock secs ( 1.52 usr +  0.00 sys =  1.52 CPU) @ 6578947.37/s (n=10000000)
as is_smart:  3 wallclock secs ( 2.67 usr +  0.00 sys =  2.67 CPU) @ 3745318.35/s (n=10000000)
   defined:  3 wallclock secs ( 1.53 usr +  0.00 sys =  1.53 CPU) @ 6535947.71/s (n=10000000)
defined_smart:  3 wallclock secs ( 2.17 usr +  0.00 sys =  2.17 CPU) @ 4608294.93/s (n=10000000)
    exists:  1 wallclock secs ( 1.33 usr +  0.00 sys =  1.33 CPU) @ 7518796.99/s (n=10000000)

使用确实存在的 key('x') 表明 'as is_smart' 是赢家。

Benchmark: timing 10000000 iterations of as is, as is_smart, defined, defined_smart, exists...
     as is:  3 wallclock secs ( 2.76 usr +  0.00 sys =  2.76 CPU) @ 3623188.41/s (n=10000000)
as is_smart:  3 wallclock secs ( 1.81 usr +  0.00 sys =  1.81 CPU) @ 5524861.88/s (n=10000000)
   defined:  3 wallclock secs ( 3.42 usr +  0.00 sys =  3.42 CPU) @ 2923976.61/s (n=10000000)
defined_smart:  2 wallclock secs ( 2.32 usr +  0.00 sys =  2.32 CPU) @ 4310344.83/s (n=10000000)
    exists:  3 wallclock secs ( 2.83 usr +  0.00 sys =  2.83 CPU) @ 3533568.90/s (n=10000000)
于 2013-08-20T14:48:02.917 回答
0

如果它不是多级哈希,您可以这样做:

$id = $ids{$name} || 'foo';

或者如果 $id 已经有一个值:

$id ||= $ids{$name};

其中 'foo' 是默认值或失败值。如果它是一个多级散列,您将使用“存在”来避免线程中前面讨论的自动激活,或者如果自动激活不会成为问题,则不使用它。

于 2009-06-11T01:17:54.437 回答
0

如果我想要高性能,我习惯于在想要创建哈希集时写这个成语:

my %h;
for my $key (@some_vals) {
  ...
  $h{$key} = undef unless exists $h{$key};
  ...
}

return keys %h;

这段代码比常用的要快一点$h{$key}++exists避免无用的分配,undef避免分配价值。对你来说最好的答案是:基准测试!我想这exists $ids{$name}比如果你有很大的未命中率要快一点$id=$ids{$name},那么你存在的版本可能比分配和测试之后更快。

例如,如果我想要快速设置交集,我会写这样的东西。

sub intersect {
  my $h;
  @$h{@{shift()}} = ();
  my $i;
  for (@_) {
    return unless %$h;
    $i = {};
    @$i{grep exists $h->{$_}, @$_} = ();
    $h = $i;
  }
  return keys %$h;
}
于 2009-06-11T09:44:14.377 回答