2012-10-17 19 views
5

Ich habe gerade Perl zu lernen begonnen und fand mich durch das Ergebnis des folgenden Code-Block zunichte gemacht:Verhalten von 'foreach' in Perl

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

Die Ausgabe lautet:

10 1 
9 2 
8 3 
7 4 

I hatte eine andere Linie erwartet: 6 5. Warum gibt es keine solche Linie? Liegt es daran, dass nach der Iteration, die 7 4 druckt, die Anzahl der Elemente im Array gleich der Anzahl der bereits ausgeführten Iterationen ist, und was Perl betrifft, ist die Schleife erledigt?

Antwort

8

Sie sollten die Liste während der Iteration wahrscheinlich nicht ändern. Von perldoc perlsyn:

Wenn irgendein Teil LIST ein Array ist, wird foreach sehr verwirrt, wenn man Elemente innerhalb der Schleife hinzugefügt oder entfernt werden, zum Beispiel mit splice. Also tu das nicht.

Wenn Sie $_ während der Schleife ausdrucken (print ("$x $y $_\n");) können Sie sehen, was los ist:

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

In diesem Fall Perl ist nur einen Index beibehalten wird, dass es das Array erhöht durch. Da Sie die Elemente von Anfang an löschen, fällt der Index am Ende der vierten Iteration vom Ende des modifizierten Arrays ab, sodass die Schleife foreach endet.

+0

Dank nneonneo. Ihre Erklärung dessen, was passiert, war genau das, was ich verstehen musste. – verbose

7

Dies ist ein gutes Beispiel dafür, was passiert, wenn Sie das Array ändern, über das Sie iterieren.

Versuchen:

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; 
} 

Oder, noch besser:

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"; 
} 

Hinweis im while Schleife oben, wobei die Anordnung @x wird interpreted in scalar context ist, daher die while-Schleife wird, bis die Anzahl von Elementen weiter in @x ist Null.

3

Sie dürfen das Array, über das Sie iterieren, nicht mit einer foreach-Schleife ändern.

Sie möchten die Array-Elemente trotzdem nicht iterieren. foreach (@x) ist eindeutig falsch. Das loopiert 10 mal, aber du willst nur 5 mal loopen.

Da Sie beim Schleifen Elemente aus dem Array entfernen, möchten Sie einfach eine Schleife erstellen, bis das Array leer ist. Um eine Bedingung bei jedem Durchlauf der Schleife zu überprüfen, verwendet man eine while-Schleife.

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

Wenn Sie eine foreach-Schleife verwenden wollte, würde es so aussehen:

my @x = 1..10; 
for ([email protected]/2) { 
    my $x = $x[-$_]; 
    my $y = $x[$_-1]; 
    print("$x $y\n"); 
} 
Verwandte Themen