Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Additional hash lookup using 'exists'?

I sometimes access a hash like this:

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

Is that good practice? I'm a bit concerned that it contains two lookups where really one should be done. Is there a better way to check the existence and assign the value?

like image 480
Frank Avatar asked Jun 10 '09 18:06

Frank


4 Answers

By checking with exists, you prevent autovivification. See Autovivification : What is it and why do I care?.

UPDATE: As trendels points out below, autovivification does not come into play in the example you posted. I am assuming that the actual code involves multi-level hashes.

Here is an illustration:

#!/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' => {}
    }
};
like image 74
Sinan Ünür Avatar answered Oct 17 '22 23:10

Sinan Ünür


You could use apply Hash::Util's lock_keys to the hash. Then perform your assignments within an 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'
like image 38
dwarring Avatar answered Oct 17 '22 22:10

dwarring


You can do it with one lookup like this:

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

However, I wouldn't bother unless I saw that that was a bottleneck

like image 1
Nathan Fellman Avatar answered Oct 17 '22 22:10

Nathan Fellman


performance is not important in this case see "Devel::NYTProf". But to answer your question:

if the value in the hash does not exists, "exists" is very fast

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

but if it does exists a second lookup is done. if the value is likely to exists than making only one look up will be faster

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

see this littel benchmark from a perl mailing list.

#!/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;
        },

};

using a key('xx') that does not exists shows that 'exists' is the winner.

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)

using a key('x') that does exists shows that 'as is_smart' is the winner.

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)
like image 1
key_ Avatar answered Oct 17 '22 22:10

key_