2009-06-10 13 views
5

volte mi accedere un hash come questo:Ricerca hash aggiuntiva usando 'esiste'?

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

è che una buona pratica? Sono un po 'preoccupato che contenga due lookup in cui dovrebbe essere fatto davvero uno. C'è un modo migliore per verificare l'esistenza e assegnare il valore?

risposta

10

controllando con exists, si impedisce autovivificazione. Vedi Autovivification : What is it and why do I care?.

UPDATE: As trendels indica quanto segue, l'autovivificazione non entra in gioco nell'esempio pubblicato. Suppongo che il codice effettivo coinvolga gli hash a più livelli.

Ecco un esempio:

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

È "esiste" meno costoso rispetto al recupero effettivo del valore? Dopo tutto, non deve seguire un elenco collegato quando trova una collisione. – Frank

+1

In questo caso particolare, tuttavia, la chiave di hash per "$ name" non dovrebbe * essere * creata dall'autovivificazione. Solo cercando di accedere a una chiave nidificata a un livello più profondo, come "$ id = $ ids {$ name} {altro}" creerebbe la chiave "$ name". – trendels

+0

@trendels Corretto ma ho pensato che l'OP fosse troppo semplificato. Tuttavia, avrei dovuto farlo notare. –

1

È possibile farlo con una ricerca come questa:

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

Tuttavia, non vorrei perdere tempo a meno che non ho visto che quello era un collo di bottiglia

+0

Ok, ma in realtà non è esattamente la stessa cosa. 'exists' controlla se c'è un valore (può essere undef), mentre controlli definiti se c'è un valore e non è undef. – Frank

+0

Hai un punto, ma alla fine della giornata se non esiste o se esiste ma non è definito, otterrai undef. Stai vedendo un successo in termini di prestazioni qui che sei così preoccupato, o è puramente accademico? Chiedo solo per curiosità, nient'altro ... –

+1

Puramente accademico! Non mi piace il fatto che sto controllando l'hash due volte. Lo cambierò e eseguirò un controllo 'defined', come suggerito tu. – Frank

0

se non è un hash multi-livello si può fare questo:

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

o se $ id ha già un valore:

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

dove 'foo' è un valore predefinito o di fall-through. Se si tratta di un hash multilivello, si dovrebbe usare "esiste" per evitare l'autovivificazione discussa in precedenza nella discussione o non utilizzarla se l'autovivificazione non sarà un problema.

+0

p.s. non ci sono "discussioni". Le domande hanno risposte, risposte e domande hanno commenti. Nessun filo. –

+0

anche ps. non c'è "prima", vedi le schede "più vecchie", "più recenti" e "voti" che schiacciano l'ordine. –

+0

Cosa succede quando $ id {$ nome} è 0 o vuoto o undef? – innaM

1

È possibile utilizzare applicare lock_keys Hash::Util all'hash. Quindi esegui i tuoi compiti all'interno di una valutazione.

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

Se voglio elevate prestazioni Sono abituato a scrivere questo linguaggio quando vogliono creare hash come set:

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

return keys %h; 

Questo codice è po 'più veloce di quanto comunemente usato $h{$key}++. exists evita l'assegnazione inutile e undef evita l'allocazione per il valore. La migliore risposta per te è: Benchmark! Immagino che exists $ids{$name} sia un po 'più veloce di $id=$ids{$name} e se hai un grosso rapporto di mancato recapito la tua versione con esiste può essere più veloce dell'assegnazione e prova dopo.

Ad esempio se voglio l'intersezione dei set veloci, avrei scritto qualcosa di simile.

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

Generalmente, le chiavi che non sono già presenti nell'hash non sono una buona idea. Non vuoi iniziare a memorizzare le chiavi che non vuoi. –

+0

Non si capisce male. Provaci ancora. –

+0

Quella parola non significa quello che pensi che significhi. – Ether

0

le prestazioni non sono importanti in questo caso vedere "Devel :: NYTProf". Ma per rispondere alla tua domanda:

se il valore di hash non esiste, "esiste" è molto veloce

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

ma se lo fa esiste una seconda ricerca è fatto. se il valore è probabile che esiste di fare un solo sguardo fino sarà più veloce

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

vedere questo benchmark littel da un 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; 
     }, 

}; 

utilizzando una chiave ('xx') che non esiste indica che 'esiste' è il vincitore.

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) 

utilizzando una chiave ('x') che esiste indica che 'as is_smart' è il vincitore.

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) 
Problemi correlati