2009-06-10 23 views
5

Đôi khi tôi truy cập một số băm như sau:Tìm kiếm băm bổ sung bằng cách sử dụng 'tồn tại'?

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

Thực tiễn đó có tốt không? Tôi có một chút lo ngại rằng nó chứa hai tra cứu, nơi thực sự nên được thực hiện. Có cách nào tốt hơn để kiểm tra sự tồn tại và gán giá trị?

Trả lời

10

Bằng cách kiểm tra với exists, bạn ngăn autovivification. Xem Autovivification : What is it and why do I care?.

CẬP NHẬT: Như trendels chỉ ra bên dưới, tự động hóa không được đưa vào ví dụ bạn đã đăng. Tôi giả định rằng mã thực sự liên quan đến băm đa cấp.

Dưới đây là một ví dụ:

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

Là 'tồn tại' rẻ hơn so với thực sự lấy giá trị? Sau khi tất cả, nó không phải làm theo một danh sách liên kết khi nó tìm thấy một vụ va chạm. – Frank

+1

Trong trường hợp cụ thể này, mặc dù, khóa băm cho "$ name" sẽ * không * được tạo bởi tự động hóa. Chỉ cố gắng truy cập khóa ở cấp độ sâu hơn, như "$ id = $ ids {$ name} {else}" sẽ tạo khóa "$ name". – trendels

+0

@trendels Đúng nhưng tôi cho rằng OP đã được đơn giản hóa quá mức. Tuy nhiên, tôi nên chỉ ra điều đó. –

1

Bạn có thể làm điều đó với một tra cứu như thế này:

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

Tuy nhiên, tôi sẽ không bận tâm, trừ khi tôi thấy rằng đó là một nút cổ chai

+0

Được rồi, nhưng điều đó thực sự không giống hệt nhau. 'tồn tại' kiểm tra nếu có một giá trị (có thể là undef), trong khi xác định kiểm tra nếu có một giá trị và nó không phải là undef. – Frank

+0

Bạn có một điểm, nhưng vào cuối ngày nếu nó không tồn tại hoặc nếu nó tồn tại nhưng không xác định, bạn sẽ nhận được một undef. Bạn có thấy một màn trình diễn ở đây khiến bạn lo ngại về điều đó, hay là điều này hoàn toàn là học thuật? Tôi hỏi chỉ vì tò mò, không có gì khác ... –

+1

Hoàn toàn học tập! Tôi chỉ không thích thực tế là tôi đang kiểm tra băm hai lần. Tôi sẽ thay đổi nó và chỉ thực hiện kiểm tra 'đã định nghĩa', như bạn đã đề xuất. – Frank

0

nếu nó không phải là một hash đa cấp, bạn có thể làm điều này:

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

hoặc nếu $ id đã có một giá trị:

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

trong đó 'foo' là giá trị mặc định hoặc thu gọn. Nếu nó là một băm đa cấp, bạn sẽ sử dụng 'tồn tại' để tránh sự tự động hóa được thảo luận trước đó trong chủ đề hoặc không sử dụng nó nếu tự động hóa không phải là vấn đề.

+0

p.s. không có "chủ đề". Các câu hỏi có câu trả lời, câu trả lời và câu hỏi có ý kiến. Không có chủ đề nào. –

+0

cũng ps. không có "sớm hơn", hãy xem các tab "cũ hơn", "mới hơn" và "phiếu bầu" để đánh dấu thứ tự. –

+0

Điều gì sẽ xảy ra khi $ id {$ name} là 0 hoặc trống hoặc không xác định? – innaM

1

Bạn có thể sử dụng các khóa lock của Hash::Util cho hàm băm. Sau đó thực hiện các bài tập của bạn trong một 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

Nếu tôi muốn hiệu suất cao Tôi đang sử dụng để viết thành ngữ này khi muốn tạo ra băm như thiết lập:

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

return keys %h; 

Mã này được nhanh hơn so với thường chút dùng $h{$key}++. exists tránh chuyển nhượng vô dụng và undef tránh phân bổ giá trị. Câu trả lời tốt nhất cho bạn là: Benchmark it! Tôi đoán rằng exists $ids{$name} nhanh hơn một chút so với $id=$ids{$name} và nếu bạn có tỷ lệ bỏ lỡ lớn, phiên bản của bạn tồn tại có thể nhanh hơn bài tập và kiểm tra sau đó.

Ví dụ: nếu tôi muốn giao điểm nhanh, tôi sẽ viết một cái gì đó như thế này.

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

Tạo các khóa không có trong băm thường không phải là một ý tưởng hay. Bạn không muốn bắt đầu lưu trữ các khóa mà bạn không muốn. –

+0

Bạn hiểu sai. Thử lại lần nữa. –

+0

Từ đó không có nghĩa là ý của bạn. – Ether

0

hiệu suất không quan trọng trong trường hợp này là "Devel :: NYTProf". Nhưng để trả lời câu hỏi của bạn:

nếu giá trị trong băm không tồn tại, "tồn tại" là rất nhanh

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

nhưng nếu nó tồn tại một tra cứu thứ hai được thực hiện. nếu giá trị là khả năng tồn tại vì làm cho chỉ có một cái nhìn lên sẽ nhanh hơn

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

thấy điều này chuẩn Littel từ một danh sách gửi thư 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; 
     }, 

}; 

bằng cách sử dụng khóa ('xx') không tồn tại cho thấy 'tồn tại' là người chiến thắng.

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) 

bằng cách sử dụng phím ('x') tồn tại cho thấy rằng 'is_smart' là người chiến thắng.

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) 
Các vấn đề liên quan