2009-06-16 26 views
42

Tôi có một tập lệnh Perl đếm số lần xuất hiện của các chuỗi khác nhau trong một tệp văn bản. Tôi muốn có thể kiểm tra xem một chuỗi nào đó chưa phải là một khóa trong băm. Có cách nào tốt hơn để làm điều này hoàn toàn không?Làm cách nào tôi có thể xem liệu một băm Perl đã có một khóa nhất định?

Đây là những gì tôi đang làm:

foreach $line (@lines){ 
    if(($line =~ m|my regex|)) 
    { 
     $string = $1; 
     if ($string is not a key in %strings) # "strings" is an associative array 
     { 
      $strings{$string} = 1; 
     } 
     else 
     { 
      $n = ($strings{$string}); 
      $strings{$string} = $n +1; 
     } 
    } 
} 
+4

Câu hỏi đặt ra là, tại sao bạn thậm chí còn làm phiền điều đó? Nếu nó không tồn tại thì $ n sẽ là undef. Giá trị số của Undef bằng 0, do đó, $ n + 1 = 1. Không cần phải kiểm tra xem nó có tồn tại trong băm hay không. –

Trả lời

95

Tôi tin rằng để kiểm tra xem một chìa khóa tồn tại trong một hash bạn chỉ cần làm

if (exists $strings{$string}) { 
    ... 
} else { 
    ... 
} 
+13

Hãy lưu ý rằng perl sẽ tự động hóa bất kỳ khóa trung gian nào không tồn tại trong một băm đa chiều để "kiểm tra" nếu khóa bạn đang tìm kiếm trong băm cuối cùng tồn tại. Nó không phải là một vấn đề với một hash đơn giản như ví dụ này nhưng .. my% test =(); in "bar" nếu (tồn tại $ test {'foo'} {'bar'}); # perl vừa được tự động hóa phím foo để tìm kiếm thanh in "foo tồn tại ngay bây giờ và bạn có thể không mong đợi điều đó!" if (tồn tại $ test {'foo'}); – Drew

6

Tôi đoán rằng mã này nên trả lời câu hỏi của bạn:

use strict; 
use warnings; 

my @keys = qw/one two three two/; 
my %hash; 
for my $key (@keys) 
{ 
    $hash{$key}++; 
} 

for my $key (keys %hash) 
{ 
    print "$key: ", $hash{$key}, "\n"; 
} 

Đầu ra:

three: 1 
one: 1 
two: 2 

Các lặp có thể được đơn giản hóa để:

$hash{$_}++ for (@keys); 

(Xem $_ trong perlvar.) Và thậm chí bạn có thể viết một cái gì đó như thế này:

$hash{$_}++ or print "Found new value: $_.\n" for (@keys); 

Những báo cáo mỗi phím lần đầu tiên nó tìm.

+0

Vâng, điều là tôi sẽ không biết trước những gì các phím sẽ được. –

+1

Có, bạn không cần phải kiểm tra sự hiện diện của khóa cho mục đích này. Bạn có thể chỉ cần nói $ string {$ 1} ++. Nếu khóa không có ở đó, nó sẽ được thêm vào với undef là giá trị, mà ++ sẽ diễn giải là 0 cho bạn. – Arkadiy

+0

Chắc chắn. Vấn đề là bạn có thể thay thế toàn bộ cơ thể của chu kỳ của bạn (theo if) với $ string {$ 1} ++. – zoul

-1

Bạn chỉ có thể đi với:

if(!$strings{$string}) .... 
+0

Đúng cũng hoạt động. Cảm ơn! –

+7

Điều này chỉ hoạt động nếu tất cả các khóa có giá trị không sai. Nói chung, đó là một giả định xấu. Sử dụng tồn tại(), được thiết kế đặc biệt chỉ dành cho việc này. –

+2

@brian de foy - Ah ha. Tôi biết tôi không nên trả lời :-) –

9

Vâng, toàn bộ mã của bạn có thể được giới hạn:

foreach $line (@lines){ 
     $strings{$1}++ if $line =~ m|my regex|; 
} 

Nếu giá trị là không có, ++ điều hành sẽ cho rằng nó là 0 (và sau đó tăng lên 1). Nếu nó đã ở đó - nó sẽ đơn giản được tăng lên.

+0

Trong khi câu trả lời của bạn là đúng, nó trả lời câu hỏi về băm. – Chris

9

Tôi khuyên bạn không nên sử dụng if ($hash{$key}) vì nó sẽ không làm những gì bạn mong đợi nếu khóa tồn tại nhưng giá trị của nó bằng 0 hoặc trống.

+1

Những trường hợp nhất định chỉ dành cho các khóa lồng nhau. Đối với vấn đề này, tồn tại là câu trả lời. Không sử dụng cho các khóa lồng nhau trong một lần chụp. –

+1

Downvote vẫn còn hơi khắc nghiệt - cảnh báo không bị vô hiệu bởi sự đơn giản của tập lệnh trong câu hỏi này. Điểm quan trọng hơn là vấn đề sử dụng if ($ hash {$ key}) không được định nghĩa và cũng không tồn tại: vấn đề "zero nhưng true". – RET

+0

Điều "không nhưng đúng" xứng đáng là một điều tối thượng. Nhưng những gì bạn nói về tự động hóa chỉ đơn giản là sai và xứng đáng là một downvote. – innaM

Các vấn đề liên quan