2009-06-10 9 views
5

j'accéder parfois un hachage comme ceci:Recherche de hachage supplémentaire avec 'exists'?

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

Est-ce que les bonnes pratiques? Je suis un peu inquiet qu'il contient deux recherches où vraiment un devrait être fait. Existe-t-il un meilleur moyen de vérifier l'existence et d'attribuer la valeur?

Répondre

10

En vérifiant avec exists, vous empêchez autovivification. Voir Autovivification : What is it and why do I care?.

MISE À JOUR: Comme le montre trendels ci-dessous, l'autovivification ne joue aucun rôle dans l'exemple que vous avez posté. Je suppose que le code réel implique des hachages à plusieurs niveaux.

Voici une 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' => {} 
    } 
}; 
+1

Existe-t-il moins cher que de récupérer la valeur? Après tout, il n'a pas à suivre une liste liée lorsqu'il trouve une collision. – Frank

+1

Dans ce cas particulier, cependant, la clé de hachage pour "$ name" ne serait * pas * créée par autovivification. Essayer seulement d'accéder à une clé imbriquée d'un niveau plus profond, comme "$ id = $ ids {$ nom} {autre}" créerait la clé "$ name". – trendels

+0

@trendels Correct mais j'ai supposé que l'OP avait trop simplifié. Pourtant, j'aurais dû le signaler. –

1

Vous pouvez le faire avec une recherche comme ceci:

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

Cependant, je ne viendrais pas à moins que je voyais que c'était un goulot d'étranglement

+0

D'accord, mais ce n'est pas exactement la même chose. 'exists' vérifie s'il y a une valeur (peut être undef), alors que défini vérifie s'il y a une valeur et ce n'est pas undef. – Frank

+0

Vous avez un point, mais à la fin de la journée s'il n'existe pas ou s'il existe mais n'est pas défini, vous obtiendrez un indéfini. Est-ce que vous voyez un coup de performance ici qui vous inquiète tellement, ou est-ce purement académique? Je demande juste par curiosité, rien d'autre ... –

+1

Purement académique! Je n'ai pas aimé le fait que je vérifie le hash deux fois. Je vais le changer et faire une vérification 'définie ', comme vous l'avez suggéré. – Frank

0

si ce n'est pas un hachage à plusieurs niveaux, vous pouvez le faire:

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

ou si id $ a déjà une valeur:

$id ||= $ids{$name}; 
Où 'foo' est une valeur par défaut ou une valeur d'accompagnement.

Si c'est un hachage à plusieurs niveaux, vous utiliserez 'exists' pour éviter l'autovivification discutée plus haut dans le thread ou ne pas l'utiliser si l'autovivification ne va pas poser de problème.

+0

p.s. il n'y a pas de "threads". Les questions ont des réponses, les réponses et les questions ont des commentaires. Aucun thread. –

+0

aussi ps. il n'y a pas d'onglet "plus tôt", voir "plus ancien", "plus récent" et "votes" qui écrasent la commande. –

+0

Que se passe-t-il lorsque $ ids {$ name} est 0 ou vide ou undef? – innaM

1

Vous pouvez utiliser les touches lock_keys de Hash::Util pour le hachage. Ensuite, effectuez vos devoirs dans un 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' 
0

Si je veux haute performance, je suis habitué à écrire cet idiome quand voulez créer hachage comme jeu:

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

return keys %h; 

Ce code est peu plus rapide que couramment utilisé $h{$key}++. exists évite l'affectation inutile et undef évite l'allocation pour la valeur. La meilleure réponse pour vous est: Benchmark it! Je suppose que exists $ids{$name} est un peu plus rapide que $id=$ids{$name} et si vous avez un grand ratio miss votre version avec existe peut être plus rapide que l'attribution et le test après. Par exemple, si je veux des intersections rapides, j'écrirais quelque chose comme ça.

sub intersect { 
    my $h; 
    @$h{@{shift()}} =(); 
    my $i; 
    for (@_) { 
    return unless %$h; 
    $i = {}; 
    @$i{grep exists $h->{$_}, @$_} =(); 
    $h = $i; 
    } 
    return keys %$h; 
} 
+1

Créer des clés qui ne sont pas déjà dans le hachage n'est généralement pas une bonne idée. Vous ne voulez pas commencer à stocker les clés que vous ne voulez pas. –

+0

Vous vous méprenez. Essaye encore. –

+0

Ce mot ne signifie pas ce que vous pensez que cela signifie. – Ether

0

la performance n'est pas importante dans ce cas, voir "Devel :: NYTProf". Mais pour répondre à votre question:

si la valeur du hachage n'existe pas, « existe » est très rapide

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

mais si elle ne se fait existe une seconde recherche. si la valeur est susceptible de existe que de faire un seul regard en sera plus rapide

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

voir cette référence littel à partir d'une liste de diffusion 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; 
     }, 

}; 

en utilisant une clé ('xx') qui n'existe pas montre que 'exists' est le gagnant.

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) 

En utilisant une clé ('x') qui existe, montre que 'as_smart' est le gagnant.

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) 
Questions connexes