Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Good day. I am new to Perl and I am writing some code just to get an idea of how Perl works. i am faced with the following problem: I am using a foreach loop and within that loop, popping and printing the contents of an array. However, the loop does not continue to the end of the array and some stuff is still left on the array. Could someone please tell me why this is possibly happening? Code snippet and output below. Thanks alot. -MisterT
#arrays as stacks my @stack1 = qw/this is a good feature/; print "initial array contains : @stack1\n"; push(@stack1,"in"); print "@stack1\n"; push(@stack1,"perl"); print "@stack1\n"; my $var = pop(@stack1); print "$var popped of arraystack which now contains : @stack1\n"; push(@stack1, 7); push(@stack1, 8); $var = pop(@stack1) + pop(@stack1); print"$var\n"; #arrays as queues print "@stack1\n"; unshift(@stack1,"thing"); print "@stack1\n"; foreach(@stack1){ my $qq = pop(@stack1); print "$qq popped from end of queue\n"; print "left on stack : @stack1 \n"; print "stack size is $#stack1 \n"; }
##output initial array contains : this is a good feature this is a good feature in this is a good feature in perl perl popped of arraystack which now contains : this is a good feature +in 15 this is a good feature in thing this is a good feature in in popped from end of queue left on stack : thing this is a good feature stack size is 5 feature popped from end of queue left on stack : thing this is a good stack size is 4 good popped from end of queue left on stack : thing this is a stack size is 3 a popped from end of queue left on stack : thing this is stack size is 2 C:\Documents and Settings>

Replies are listed 'Best First'.
Re: using foreach
by psini (Deacon) on Jul 25, 2008 at 11:18 UTC
    foreach(@stack1){ my $qq = pop(@stack1); print "$qq popped from end of queue\n"; print "left on stack : @stack1 \n"; print "stack size is $#stack1 \n"; }

    You are iterating on @stack1 and, at each iteration, tou pop the last element from the stack. When foreach reaches the point where elements have been popped it stops.

    You can try:

    while(@stack1){ my $qq = pop(@stack1); print "$qq popped from end of queue\n"; print "left on stack : @stack1 \n"; print "stack size is $#stack1 \n"; }

    So your loop iterates until @stack1 is empty.

    Rule One: "Do not act incautiously when confronting a little bald wrinkly smiling man."

      Thanks for your wisdom.. much appreciated. -MisterT
Re: using foreach
by pjotrik (Friar) on Jul 25, 2008 at 11:57 UTC
    Manipulating the array you're iterating over is generally not a good idea. If what you want is to process the elements of the array beginning from the end, use either while as advised by psini or use for/foreach on a reversed list (and do not pop the elements, it's not necessary).
    foreach(reverse @stack1){ print "found $_\n"; }
Re: using foreach
by kabeldag (Hermit) on Jul 25, 2008 at 11:50 UTC
    Yep, it's just an iteration mishap that you have incurred. See Foreach.

    Better to use functions like pop and shift within while loops.

    They are a cause of confusion, sometimes. Myself, in most instances, I usually tend to use normal iteration methods through a list/structure (mostly).

Re: using foreach
by broomduster (Priest) on Jul 25, 2008 at 12:20 UTC
    One additional Perl-ish point for you to be aware of here... :-)

    $#stack1 is the index of the last element of the array @stack1. But the stack size is arguably the length of the array, which is given by @stack1 in a scalar context (i.e,  scalar @stack1 to be unambiguous).

    Happy Perl-ing.

Re: using foreach
by thezip (Vicar) on Jul 25, 2008 at 23:57 UTC

    Indeed, it is not a good idea to manipulate a stack unless you follow the specific interface that applies to a stack:

    1. A stack is like a stack of dishes in that you may only place items onto the top of the stack, and you may only pop items from the top of the stack.

    2. You must always check to see if the stack isEmpty() before attempting to pop.

    3. If desired, you may implement a sizeOf feature, and its purpose is to return the current number of items in the stack. (This count can be used to implement the isEmpty() function.)

    In your code, I do not see any place where you check for the empty stack, so in another context (where it's not obvious that there are items on the stack), it would be a potential error to attempt to pop from a potentially empty stack.

    While it is true that a "while loop" is would act in this capacity, you have used a "for loop", and should therefore check for empty prior to a pop.


    Your wish is my commandline.
Re: using foreach
by GrandFather (Saint) on Jul 25, 2008 at 22:00 UTC

    A few points unrelated to your immediate problem:

    • A favourite mantra here: Always use strictures (use strict; use warnings; - see The strictures, according to Seuss).
    • Most Perl functions are generally written without parenthesis: push @stack1, "Perl" for example.
    • If you simply want to iterate some number of times use for (1 .. @stack1) {...}. Perl uses internal magic to optimise the loop and as it happens, it resolves your issue. The optimisation is generally unimportant. The clarity of intent and correct behaviour are compelling.

    Minor variations on the line:

    $var = pop (@stack1) + pop (@stack1);

    will surprise you one day. It is not defined in what order the pops are executed. If the operator were / or . then the result may be quite different than you expected! The same issue occurs in the evaluation order of parameter lists and can cause very subtle bugs.


    Perl reduces RSI - it saves typing