Beefy Boxes and Bandwidth Generously Provided by pair Networks chromatic writing perl on a camel
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Why does a full iteration over array & shift is not emptying the array

by Anonymous Monk
on May 04, 2010 at 13:27 UTC ( #838307=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Am sure this is a very simple question, for all you guys ..

But for some reason am not able to figure out, so please forgive me if it is too simple.

Question is, why the following code is not shifting all the data out ?! and keeps the last element of the array as it is even after a full iteration through the array and shift !
@arr = ( 1, 2, 3 ); for my $x ( @arr ) { print "Shifting\n"; shift @arr; print "count: " . scalar @arr; print "data: @arr\n"; } print "\ndata: @arr";


The last print is printing 3 ? I expect it to print nothing ? Why it is not doing so ?

Can anybody explain it better !

Comment on Why does a full iteration over array & shift is not emptying the array
Download Code
Re: Why does a full iteration over array & shift is not emptying the array
by moritz (Cardinal) on May 04, 2010 at 13:39 UTC
    Modifying the array you're iterating over is a bad idea.

    The explanation here is that $x is bound to the first array item (1) in the first iteration, and to the second array item (which is then 3) in the second iteration. At that time there's no third item in the array anymore, and the body of the loop is executed only twice (as you can see from your debugging output).

    It becomes a bit clearer if you also print out $x in the loop body.

Re: Why does a full iteration over array & shift is not emptying the array
by AR (Friar) on May 04, 2010 at 13:42 UTC

    The problem is you're trying to iterate over an array that you're changing. You enter your loop and set $x to 1 (which occupies index 0). You then shift '1' out and lower the indices of all remaining elements. When you hit the beginning of the loop again, $x is set to 3 because it's now in index 1. You then shift 2 out and your loop exits because there's nothing at index 2 (or index 1 for that matter).

    If you're trying to empty the array, you could try:

    my @arr = ( 1, 2, 3 ); for my $x ( shift @arr ) { #do some stuff }

    Edit: What the hell was I thinking when I wrote that? I meant to write the while loop that BrowserUk wrote below. Please do not use my answer. It doesn't work.

Re: Why does a full iteration over array & shift is not emptying the array
by BrowserUk (Pope) on May 04, 2010 at 13:53 UTC

    Use a while loop instead of a for loop:

    @a = 1 .. 10;; while( defined( my $x = shift @a ) ) { print $x };; 1 2 3 4 5 6 7 8 9 10 print scalar @a;; 0

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Why does a full iteration over array & shift is not emptying the array
by toolic (Chancellor) on May 04, 2010 at 14:18 UTC
    ... and here is a quote from the documentation (For Loops):
    If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.
Re: Why does a full iteration over array & shift is not emptying the array
by PeterPeiGuo (Hermit) on May 04, 2010 at 14:19 UTC
    One trick people often use in this situation is to go in the reverse order, and this is a common trick across most languages:
    use warnings; use strict; use Data::Dumper; my @arr = ( 1, 2, 3 ); for (my $i = scalar @arr - 1; $i >= 0; $i --) { my $a = pop @arr; print "$a\n"; print Dumper(\@arr); } print Dump(@arr);

    Peter (Guo) Pei

Re: Why does a full iteration over array & shift is not emptying the array
by rubasov (Friar) on May 04, 2010 at 14:26 UTC
    Another way to do it (this won't fail if your array contains undefs):
    my @a = ( 1 .. 4, undef, 6 .. 10 ); while (@a) { my $x = shift @a; say defined $x ? $x : 'undef'; }
Re: Why does a full iteration over array & shift is not emptying the array
by JavaFan (Canon) on May 04, 2010 at 14:31 UTC
    Why is already explained by others. Here's another alternative:
    for my $x (@{[]} = @arr) { ... }
    this makes a copy of the elements of @arr to loop over, without using a (named) variable.

      FWIW, adding an empty list also makes a copy of the elements (on the internal stack) to iterate over:

      for my $x ((), @arr) { ... }
Re: Why does a full iteration over array & shift is not emptying the array
by Marshall (Prior) on May 04, 2010 at 14:57 UTC
    The whole idea of a foreach my $x (@arr){} iterator is so that you don't have to know the number of elements in @arr to process each one. In many languages like C, you have to setup something like:
    for (i=0;i<=max;i++){..do something with x[i], etc..}.

    In Perl, the foreach loop causes $x to become each thing in the array successively without: a)modifying the size of @arr (the $x value can be use to modify a value in @arr); b) needing a subscript and c)perhaps more importantly, you don't have to worry about the ending condition being: i<=max or i<max! "off-by-one" is a VERY common error.

    If you want to keep track of how many x's there have been processed so far, then something like this is a better way to go because the looping construct doesn't depend upon the $count value, meaning that you won't "overflow" the array bounds even if your count should have started at 0 instead of 1 or maybe a --$count vs $count++ boo-boo:

    my @arr =(10,11,12); my $count =1; foreach my $x (@arr) { print $count++," $x\n"; } #prints #1 10 #2 11 #3 12
    If you are going to actually reduce the size (num of elements) in @arr instead of just iterating over each element, then use a "while" loop. The following processes pairs of things from @tokens and removes them from @tokens as it does so.
    while (@tokens) { my ($x, $y) = splice(@tokens,0,2); .....use $x and $y to do something... ..next loop causes another pair of $x, $y to be used if there are any left in @tokens }
    Update: To simplify the above to one shift (instead of 2 using splice()), this is completely legal although not a normal situation:
    #!/usr/bin/perl -w use strict; my @arr = ( 11, 12, 13, 14 ); while (@arr) { my $x = shift(@arr); print "this item=$x #left in arr ".@arr."\n"; } =prints this item=11 #left in arr 3 this item=12 #left in arr 2 this item=13 #left in arr 1 this item=14 #left in arr 0 =cut

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://838307]
Approved by Corion
Front-paged by Ratazong
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (6)
As of 2014-04-17 07:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (440 votes), past polls