On Tue, Dec 03, 2002 at 04:40:37PM +0100, Jenda Krynicky wrote:

[ Rearranging slightly to get your summary first ]

> The documentation says you should not do it. Not because it would be 
> that hard to say what happens (once you understand what did I say 
> above), but because it would be easy to create hard to find infinite 
> loops:

There is another, more important reason.  A bug was recently reported
which Dave Mitchell, one of the Perl maintainers categorised as:

] Unfortunately, this is one of a long list of well-known coredumps that can
] occur when the array being iterated is modified. In the short term, just
] don't do it! In the longer term, Perl ought to avoid such coredumps. (But
] it isn't easy to achieve this efficiently)

When the docs say "don't do that" they really mean it! :-)

It may be that Perl 5.10 will fix some of those coredumps, but in doing
so the behaviour of this construct might change.

> If would help a lot if you tried to find out what's going on:
> 
>       @a = (1,2,3);
>       foreach my $loopvar (@a) {
>               print "loop var: $loopvar\n";
>               print "poped: ".pop(@a)."\n";
>               print "\@a: (" . join(', ',@a) . ")\n\n";
>       }
> 
> This prints:
>       loop var: 1
>       poped: 3
>       @a: (1, 2)
> 
>       loop var: 2
>       poped: 2
>       @a: (1)
> 
> Which is exactly what I would expect and the only sensible thing to 
> do. It iterates on the array and you pop the last elements at the 

It might be exactly what you expect, but that doesn't mean that it is
the only sensible thing to do ;-)

> same time. Therefore in the first iteration it sets the $loopvar to 
> the first element of the array and removes the last element (3rd 
> element), in the second iteration it sets $loopvar to the second 
> element and again removes the last element (2nd elemenr). And of 
> course there'll be no more iterations because the array has only one 
> element now and the loop wants the third element.
> 
> The only thing you have to remember here is that the foreach(){} 
> doesn't make a copy of the array it iterates over. Therefore if you 
> change the array somehow the foreach(){} will operate on the modified 
> array. See this:
> 
>       @a = (1,2,3,4,5);
>       foreach my $loopvar (@a) {
>               print "loop var: $loopvar\n";
>               print "shifted: ".shift(@a)."\n\n";
>       }
> 
> Look what it prints! Now that seems confusing at first. but only if 
> you do not remember that the foreach(){} only remembers its POSITION 
> in the array, not the actual element. Therefore if you shift() off 
> the first element, you move the other elements!

This explanation is all well and good, but of course, in internals land
things get a bit more complicated.  Keep experimenting, and you'll hit
corner cases and get core dumps.

Really, just "don't do that".  You'll be glad in the end ;-)

-- 
Paul Johnson - [EMAIL PROTECTED]
http://www.pjcj.net

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to