【问题标题】:Additional hash lookup using 'exists'?使用“存在”的附加哈希查找?
【发布时间】:2010-11-01 22:04:42
【问题描述】:

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

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

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

【问题讨论】:

    标签: performance perl hash lookup exists


    【解决方案1】:

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

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

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

    【讨论】:

    • 好的,但这实际上并不完全相同。 exists 检查是否有一个值(可能是 undef),而定义检查是否有一个值并且它不是 undef。
    • 你有一个观点,但最终如果它不存在或存在但未定义,你将得到一个 undef。您是否看到这里的表现受到如此关注,或者这纯粹是学术性的?我只是出于好奇而问,没有别的......
    • 纯学术!我只是不喜欢我检查哈希两次的事实。我会更改它,然后按照您的建议进行defined 检查。
    【解决方案2】:

    通过检查exists,您可以防止自动复活。见Autovivification : What is it and why do I care?

    更新:正如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' => {}
        }
    };
    

    【讨论】:

    • exists 是否比实际检索价值便宜?毕竟,它发现碰撞时不必遵循链表。
    • 在这种特殊情况下,“$name”的哈希键不会通过自动激活创建。仅尝试访问嵌套更深一层的键,例如“$id = $ids{$name}{other}”会创建“$name”键。
    • @trendels 正确,但我认为 OP 过于简化了。不过,我应该指出这一点。
    • 感谢您的链接。我没有意识到这一点。
    【解决方案3】:

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

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

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

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

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

    【讨论】:

    • 附注没有“线程”。问题有答案,答案和问题有 cmets。没有线程。
    • 还有ps。没有“较早”,请参阅“较旧”、“较新”和“投票”选项卡,这些选项卡混合了订单。
    • 当 $ids{$name} 为 0 或为空或 undef 时会发生什么?
    【解决方案4】:

    您可以使用将Hash::Util 的lock_keys 应用于哈希。然后在 eval 中执行你的任务。

    #!/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'
    

    【讨论】:

      【解决方案5】:

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

      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;
      }
      

      【讨论】:

      • 创建不在散列中的键通常不是一个好主意。您不想开始存储您不想要的密钥。
      • 你误会了。再试一次。
      • 那个词并不像你想象的那样。
      【解决方案6】:

      在这种情况下,性能并不重要,请参阅“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)
      

      【讨论】:

        猜你喜欢
        • 2014-08-06
        • 1970-01-01
        • 1970-01-01
        • 2011-11-11
        • 2011-12-26
        • 1970-01-01
        • 1970-01-01
        • 2011-12-28
        • 1970-01-01
        相关资源
        最近更新 更多