2010-03-07 17 views
11

Từ perldoc -f each chúng ta đọc:Là perl của mỗi chức năng có giá trị sử dụng?

Có một iterator duy nhất cho mỗi băm, chia sẻ bởi tất cả each, keysvalues gọi hàm trong chương trình; nó có thể được đặt lại bằng cách đọc tất cả các phần tử từ băm hoặc bằng cách đánh giá keys HASH hoặc values HASH.

Các iterator chưa cài lại khi bạn rời khỏi phạm vi chứa each(), và điều này có thể dẫn đến lỗi:

my %h = map { $_, 1 } qw(1 2 3); 
while (my $k = each %h) { print "1: $k\n"; last } 
while (my $k = each %h) { print "2: $k\n"  } 

Output:

1: 1 
2: 3 
2: 2 

cách giải quyết chung cho việc này là gì hành vi? Và có đáng để sử dụng each nói chung không?

+5

Tôi sẽ hình dung rằng các cách giải quyết thông thường bao gồm việc đánh giá 'các phím HASH' hoặc' các giá trị HASH' –

Trả lời

11

Tôi nghĩ rằng nó đáng để sử dụng miễn là bạn biết về điều này. Đó là lý tưởng khi bạn cần cả hai quan trọng và giá trị trong lần lặp:

while (my ($k,$v) = each %h) { 
    say "$k = $v"; 
} 

Trong ví dụ của bạn, bạn có thể thiết lập lại các iterator bằng cách thêm keys %h; như vậy:

my %h = map { $_ => 1 } qw/1 2 3/; 
while (my $k = each %h) { print "1: $k\n"; last } 
keys %h; # reset %h 
while (my $k = each %h) { print "2: $k\n" } 

Từ Perl 5,12 each cũng sẽ cho phép lặp trên một mảng.

2

sử dụng chức năng keys() để đặt lại trình lặp. Xem faq để biết thêm

8

tôi thấy each là rất tiện dụng cho các thành ngữ như thế này:

my $hashref = some_really_complicated_method_that_builds_a_large_and_deep_structure(); 
while (my ($key, $value) = each %$hashref) 
{ 
    # code that does stuff with both $key and $value 
} 

Contrast rằng mã này:

my $hashref = ...same call as above 
foreach my $key (keys %$hashref) 
{ 
    my $value = $hashref->{$key}; 
    # more code here... 
} 

Trong trường hợp đầu tiên, cả hai $key$value ngay lập tức có sẵn cho phần thân của vòng lặp. Trong trường hợp thứ hai, trước tiên, bạn phải tìm nạp trước số $value. Ngoài ra, danh sách các phím của $hashref có thể rất lớn, chiếm bộ nhớ. Điều này đôi khi là một vấn đề. each không phải chịu chi phí như vậy.

Tuy nhiên, những hạn chế của each không rõ ràng ngay lập tức: nếu hủy bỏ vòng lặp sớm, trình lặp của hàm băm không được đặt lại. Ngoài ra (và tôi thấy điều này nghiêm trọng hơn và thậm chí ít nhìn thấy được): bạn không thể gọi số keys(), values() hoặc số khác each() từ vòng lặp này. Để làm như vậy sẽ đặt lại trình lặp, và bạn sẽ mất vị trí của mình trong vòng lặp while. Vòng lặp while sẽ tiếp tục mãi mãi, đó chắc chắn là một lỗi nghiêm trọng.

7

mỗi không chỉ đáng sử dụng, nó là khá nhiều bắt buộc nếu bạn muốn lặp qua tất cả một băm gắn quá lớn cho bộ nhớ.

Một khóa void-context() (hoặc giá trị, nhưng nhất quán là tốt đẹp) trước khi bắt đầu vòng lặp là "workaround" duy nhất cần thiết; là có một số lý do bạn đang tìm kiếm một số workaround khác?

+0

Điểm tuyệt vời! Đây là lý do tốt nhất (chỉ?) Để sử dụng 'mỗi' tôi có thể nghĩ đến. – daotoad

1

Tốt nhất nếu được sử dụng là tên: each. Đó có thể là điều sai lầm khi sử dụng nếu bạn muốn nói "cho tôi cặp khóa-giá trị đầu tiên" hoặc "cho tôi hai cặp đầu tiên" hoặc bất kỳ thứ gì. Chỉ cần nhớ rằng ý tưởng đủ linh hoạt mỗi khi bạn gọi nó, bạn sẽ nhận được cặp tiếp theo (hoặc chính trong ngữ cảnh vô hướng).

6

each quá nguy hiểm khi sử dụng và nhiều hướng dẫn về phong cách cấm sử dụng hoàn toàn. Nguy hiểm là nếu một chu kỳ each bị hủy trước khi kết thúc băm, chu kỳ tiếp theo sẽ bắt đầu ở đó. Điều này có thể gây ra lỗi rất khó sinh sản; hành vi của một phần của chương trình sẽ phụ thuộc vào một phần hoàn toàn không liên quan khác của chương trình. Bạn có thể sử dụng each ngay, nhưng điều gì về mỗi mô-đun đã từng viết có thể sử dụng hàm băm của bạn (hoặc hashref; nó giống nhau)?

keysvalues luôn an toàn, vì vậy, chỉ cần sử dụng chúng. keys làm cho việc di chuyển băm theo thứ tự xác định dễ dàng hơn, điều này hầu như luôn hữu ích hơn. (for my $key (sort keys %hash) { ... })

+0

sử dụng rất nhiều băm toàn cầu, phải không? – ysth

+2

Không quan trọng nếu nó là toàn cầu hay không. Ngay cả các thuộc tính riêng của một lớp cũng dễ bị tổn thương với vấn đề này. Bất cứ điều gì trả về một tham chiếu đến một băm đều bị ảnh hưởng. – jrockway

1

each có biến buit-in, ẩn toàn cầu có thể làm tổn thương bạn. Trừ khi bạn cần hành vi này, an toàn hơn khi chỉ sử dụng keys.

Hãy xem xét ví dụ này, nơi chúng tôi muốn nhóm cặp k/v chúng tôi (vâng, tôi biết printf sẽ làm điều này tốt hơn):

#!perl 

use strict; 
use warnings; 

use Test::More 'no_plan'; 

{ my %foo = map { ($_) x 2 } (1..15); 

    is(one(\%foo), one(\%foo), 'Calling one twice works with 15 keys'); 
    is(two(\%foo), two(\%foo), 'Calling two twice works with 15 keys'); 
} 

{ my %foo = map { ($_) x 2 } (1..105); 

    is(one(\%foo), one(\%foo), 'Calling one twice works with 105 keys'); 
    is(two(\%foo), two(\%foo), 'Calling two twice works with 105 keys'); 
} 


sub one { 
    my $foo = shift; 

    my $r = ''; 

    for(1..9) { 
     last unless my ($k, $v) = each %$foo; 

     $r .= " $_: $k -> $v\n"; 
    } 
    for(10..99) { 
     last unless my ($k, $v) = each %$foo; 

     $r .= " $_: $k -> $v\n"; 
    } 

    return $r; 
} 

sub two { 
    my $foo = shift; 

    my $r = ''; 

    my @k = keys %$foo; 

    for(1..9) { 
     last unless @k; 
     my $k = shift @k; 

     $r .= " $_: $k -> $foo->{$k}\n"; 
    } 
    for(10..99) { 
     last unless @k; 
     my $k = shift @k; 

     $r .= " $_: $k -> $foo->{$k}\n"; 
    } 

    return $r; 
} 

Gỡ rối các lỗi trình bày trong các bài kiểm tra trên trong một ứng dụng thực tế sẽ đau đớn khủng khiếp. (Đối với tốt hơn sử dụng đầu ra Test::Differenceseq_or_diff thay vì is.)

Tất nhiên one() thể được cố định bằng cách sử dụng keys để xóa các iterator lúc bắt đầu và kết thúc của chương trình con. Nếu bạn nhớ. Nếu tất cả đồng nghiệp của bạn nhớ. Nó hoàn toàn an toàn miễn là không ai quên.

Tôi không biết về bạn, nhưng tôi sẽ chỉ sử dụng keysvalues.

1

mỗi() có thể hiệu quả hơn nếu bạn đang lặp qua một hàm băm gắn, ví dụ như một cơ sở dữ liệu chứa hàng triệu khóa; bằng cách đó bạn không phải tải tất cả các phím trong bộ nhớ.

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