2012-10-17 33 views
5

Tôi vừa bắt đầu học Perl và tìm thấy bản thân mình cản trở bởi kết quả của khối mã sau đây:Hành vi của 'foreach' trong Perl

@x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
foreach (@x) { 
    $x = pop (@x) ; 
    $y = shift (@x); 
    print ("$x $y \n"); 
} 

Đầu ra là:

10 1 
9 2 
8 3 
7 4 

tôi đã mong đợi một dòng khác: 6 5. Tại sao không có một dòng như vậy? Có phải vì sau khi lặp lại mà in 7 4, số lượng các phần tử còn lại trong mảng bằng với số lần lặp đã hoàn thành, và như vậy theo như Perl có liên quan, vòng lặp được thực hiện?

Trả lời

8

Có thể bạn không nên sửa đổi danh sách trong khi lặp lại. Từ perldoc perlsyn:

Nếu bất kỳ phần nào của LIST là một mảng, foreach sẽ nhận được rất bối rối nếu bạn thêm hoặc loại bỏ các yếu tố trong cơ thể lặp, ví dụ với splice. Vậy đừng làm thế.

Nếu bạn in ra $_ trong vòng lặp (print ("$x $y $_\n");), bạn có thể xem những gì đang xảy ra:

10 1 1 
9 2 3 
8 3 5 
7 4 7 

Trong trường hợp này Perl chỉ được duy trì một chỉ số mà nó increments qua mảng. Vì bạn xóa các phần tử ngay từ đầu, chỉ mục rơi khỏi phần cuối của mảng được sửa đổi ở cuối lần lặp thứ tư, và do đó vòng lặp foreach kết thúc.

+0

Cảm ơn nneonneo. Lời giải thích của bạn về những gì xảy ra chính xác là những gì tôi cần phải hiểu. – verbose

7

Đây là minh họa tốt về những gì sẽ xảy ra khi bạn thay đổi mảng mà bạn đang lặp lại.

Hãy thử:

use strict: #Always! 
use warnings; #Always! 

my @x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
my $n=$#x; 
foreach my $i(0..$n) { 
    my $x = pop (@x) ; 
    my $y = shift (@x); 
    print "$x $y\n" if defined $x and defined $y; 
} 

Hoặc, tốt hơn được nêu ra:

use strict; 
use warnings; 

my @x=1..10; #same thing, just written more idiomatically 

while(@x) 
{ 
    my $x=pop @x; #no parentheses needed 
    my $y=shift @x; 
    print "$x $y\n"; 
} 

Lưu ý trong while vòng lặp trên, mảng @x đang được interpreted in scalar context, do đó vòng lặp while sẽ tiếp tục cho đến khi số phần tử trong số @x bằng không.

3

Bạn không được phép sửa đổi mảng mà bạn đang lặp lại bằng cách sử dụng vòng lặp foreach.

Bạn không muốn lặp lại qua các phần tử mảng. foreach (@x) rõ ràng là sai. Vòng lặp đó 10 lần, nhưng bạn chỉ muốn lặp lại 5 lần.

Vì bạn loại bỏ các phần tử khỏi mảng khi bạn lặp, bạn chỉ muốn lặp cho đến khi mảng trống. Để kiểm tra một điều kiện tại mỗi điểm của vòng lặp, một điều kiện sử dụng một vòng lặp while.

my @x = 1..10; 
while (@x) { 
    my $x = pop(@x) ; 
    my $y = shift(@x); 
    print("$x $y\n"); 
} 

Nếu bạn muốn sử dụng một vòng lặp foreach, nó sẽ trông như thế này:

my @x = 1..10; 
for ([email protected]/2) { 
    my $x = $x[-$_]; 
    my $y = $x[$_-1]; 
    print("$x $y\n"); 
} 
Các vấn đề liên quan