ivanthemad has asked for the wisdom of the Perl Monks concerning the following question:
Most mystical of monks, I seek your wisdom today regarding what is undoubtedly an imperfect understanding of Perl arrays.
I have an array of integers, and I would like to pair every two integers. Perhaps I should simply use a for-loop, but I am curious as to why my first solution does not work.
use strict;
use warnings;
my $s = "foo bar baz qux 3 3 1 3";
my @a = split /\s/, $s;
splice(@a, 0, 4);
while (@a) {
print (shift @a), (shift @a), "\n";
}
My understanding is that the while will execute so long as there are elements in @a, and that shift will return the left-most element of the array, move all elements down by one, and shorten the array by one. I expected to receive as output:
33
13
But instead received:
31 # and no newline!
Please direct this flummoxed novice to an explanation which said novice failed to find via either RTFM or STFW. I would be most grateful!
Re: arrays: shifting in while loop
by BrowserUk (Patriarch) on Mar 06, 2012 at 14:39 UTC
|
C:\test>perl -MO=Deparse,p -e" print (shift @a), (shift @a), "\n";"
print(shift @a), shift @a, \'n';
-e syntax OK
As you can see, only the first parameter is being passed to print.
If you add another set of parens: print( (shift @a), (shift @a), "\n" );
Or drop the parens entirely: print shift @a, shift @a, "\n";
Your code will (probably) work as you expect.
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
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".
| [reply] [d/l] [select] |
|
Thank you, you are correct! Re-examining my code in the interim between posting and your timely response, I noticed that I was in fact receiving a warning:
print (...) interpreted as function at - line 7.
Thank you for the clear explanation which has rectified my misunderstanding! I will also examine further your use of -MO=Deparse,p -e, which looks to be very useful indeed. | [reply] [d/l] [select] |
|
When you get a message like "print (...) interpreted as function ...", you can typically disambiguate your statement for Perl by simply adding a + in front of the opening parenthesis.
Your original code runs like this:
ken@ganymede: ~/local/bin
$ perl -Mstrict -Mwarnings -E 'my $s = "foo bar baz qux 3 3 1 3"; my @
+a = split /\s/, $s; splice(@a, 0, 4); while (@a) { print (shift @a),
+(shift @a), "\n"; }'
print (...) interpreted as function at -e line 1.
Useless use of a constant (
) in void context at -e line 1.
31ken@ganymede: ~/local/bin
$
Adding a single + (changing ... print (... to ... print +(...), you get the output you were after:
ken@ganymede: ~/local/bin
$ perl -Mstrict -Mwarnings -E 'my $s = "foo bar baz qux 3 3 1 3"; my @
+a = split /\s/, $s; splice(@a, 0, 4); while (@a) { print +(shift @a),
+ (shift @a), "\n"; }'
33
13
ken@ganymede: ~/local/bin
$
This is discussed in more detail in perlop - Symbolic Unary Operators.
| [reply] [d/l] [select] |
|
| [reply] |
|
Do note you only get said warning because you use exactly one space between print and the parenthesis. Drop the space, use 2 spaces, or a tab, and the warning disappears. Replace print by warn, and the warning disappears as well.
| [reply] [d/l] |
Re: arrays: shifting in while loop
by kcott (Archbishop) on Mar 06, 2012 at 14:52 UTC
|
I see you got your answer as to what was happening in your code snippet from BrowserUk.
I don't know what context you want to use this in but the pairwise() function in List::MoreUtils may be helpful.
| [reply] [d/l] |
Re: arrays: shifting in while loop
by AnomalousMonk (Archbishop) on Mar 06, 2012 at 21:36 UTC
|
My understanding is that the while will execute so long as there are elements in @a...
ivanthemad: I expect you already understand the following point perfectly well, but I want to dispell my sneaking suspicion of a possible misapprehension on your part.
The while-loop in the example code will only begin execution (Update: of a loopthe loop body) if the array is non-empty. However, (Update: once begun) looploop body execution will continue regardless of the state of the array unless an explicit loop exit is made conditional upon array state, e.g., with a statement like last unless @a;.
Update: Clarified (hopefully) pertinence to loop body per repellent.
| [reply] [d/l] [select] |
|
... loop execution will continue regardless of the state of the array unless an explicit loop exit is made ...
Hmm, really? I always thought the while-loop EXPR was re-evaluated after each iteration:
my @a = (3, 4);
while (do { say "yo"; @a })
{
say "hi mom";
shift @a; # no explicit loop break out
}
say "bye mom";
__END__
yo
hi mom
yo
hi mom
yo
bye mom
| [reply] [d/l] |
|
>perl -wMstrict -le
"my @ra = (9, 8, 7);
;;
while (@ra) {
print 0+@ra, ' in @ra';
shift @ra;
print 0+@ra, ' in @ra';
shift @ra;
print 0+@ra, ' in @ra';
shift @ra;
print 0+@ra, ' in @ra';
shift @ra;
print 0+@ra, ' in @ra';
shift @ra;
print 0+@ra, ' in @ra';
}
"
3 in @ra
2 in @ra
1 in @ra
0 in @ra
0 in @ra
0 in @ra
| [reply] [d/l] |
|
|
|