Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Mysterious for behavior

by Roy Johnson (Monsignor)
on Apr 21, 2004 at 19:01 UTC ( #347092=perlquestion: print w/replies, xml ) Need Help??

Roy Johnson has asked for the wisdom of the Perl Monks concerning the following question:

A fellow who is learning Perl came to me with the discovery that using commas instead of semicolons in a for loop causes it to loop three times. That made sense to me: it was treating it as a foreach loop with three items in the list.

What didn't make sense to me was the output. The final value in the list is the only value for the variable in the loop (as if the comma were being treated as an op for a scalar expression, but it still does three iterations). This is more easily explained with code:

my $i; for ($i=3, $i=2, $i=1) { print "$_ and $i\n"; }
output is:
1 and 1 1 and 1 1 and 1
Please impart some Perl Wisdom to explain this mystery.

The PerlMonk tr/// Advocate

Replies are listed 'Best First'.
Re: Mysterious for behavior
by hardburn (Abbot) on Apr 21, 2004 at 19:08 UTC

    I belive what is happening is that each statement in the for is being evaluated from left to right, thus making the $i = 1 portion win out. The list the loop works on becomes ($i, $i, $i), which makes the complete code equivilent to:

    my $i = 1; for ( $i, $i, $i ) { print "$_ and $i\n"; }

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated


      Update 2: Because of the explanations given, I understand the phenomenon, but I think it can be explained more explicitly than has been done, so I'm going to give it a shot:

      First of all, all the expressions will be evaluated when the list is built. That is, before any iteration happens, all of those assignments have been executed.

      The next thing to understand is that lists may include ordinary values and lvalues; an assignment yields an lvalue, though an expression of which an assignment is just part (e.g., 0+($x=3)) is an ordinary value. What is stored in an lvalue can change even after the lvalue is inserted into the list, so its value depends on when you look at it.

      The aliasing of a for loop is not fundamental to this. Simply assigning the same list to an array demonstrates the same phenomenon:

      my $x; my @a = ($x=1, $x+=1, $x-=5); print "@a\n"; #yields: #-3 -3 -3
      One final interesting note: postincrement does not yield an lvalue, but preincrement does.

      The PerlMonk tr/// Advocate

        Post-increment does not yield a new lvalue, but it does have an lvalue, and does modify the variable. Update: I don't what I was thinking about there. Post-increment has an rvalue. (That's what happens when you try to post a message just before quitting time!) However, below is still semi-interesting. Consider:

        print "$_ and $i\n" for ($i = 1, $i++, $i++); __OUTPUT__ 3 and 3 1 and 3 2 and 3

        The $i is being modified, and the value before the post-increment is used in the list. Playing around, you could do something like:

        print "$_ and $i\n" for (\($i = 1, $i++, $i++)); print \$i , "\n"; __OUTPUT__ SCALAR(0x8176c44) and 3 SCALAR(0x815dcc0) and 3 SCALAR(0x815dad4) and 3 SCALAR(0x8176c44)
        This shows that the first element of the list is the $i variable, and that perl generates two temporary scalars to hold the values of $i prior to increment. Also, the entire list is evaluated before iteration.

        Interesting stuff you found here!

Re: Mysterious for behavior
by Belgarion (Chaplain) on Apr 21, 2004 at 19:11 UTC

    The comma operator in this case is presenting a list of three elements to the initializer of the for operator. If you had something like this:

    for ($i=3, $i=2, $i=1, $i=0)
    you would get four lines, because there are four elements.

    It is printing "1" for each input because the initializer list is evaulated before the iteration. Therefore, $i is set to one before the for operator iterates over the three element list.

Re: Mysterious for behavior
by borisz (Canon) on Apr 21, 2004 at 19:09 UTC
    Hi, what you really wrote is this: $_ is a alias to $i and $i is 1.
    my $i; for ( ($i=3, $i=2, $i=1) ) { print("$_ and $i\n"); }
      Actually (and this was the point of confusion), I made $_ an alias to $i three times. I would have understood if it had acted like
      for (scalar($i=3, $i=2, $i=1)) { print "$_ and $i\n"; }
      where each assignment happens in turn, because of the comma operator, and $_ is aliased to the final result ($i) once. But that didn't happen. I expected each assignment expression to yield the value of its right-hand side, but Perl evaluated it more like:
      $i=3, $i=2, $i=1; for ($i, $i, $i) { print "$_ and $i\n"; }

      The interesting part

      I can get the effect I expected by doing this:
      for (0+($i=3), 0+($i=2), $i=1) { print "$_ and $i\n"; }

      The PerlMonk tr/// Advocate
        I can get the effect I expected by doing this:
        for (0+($i=3), 0+($i=2), $i=1) { print "$_ and $i\n"; }
        In your example you create
        for ( temp, temp2, $i ) {

        Maybe a better way to think about it is that you created an array of three elements, each element is pointing to the $i variable. However, because you are initializing $i for each element in the list, the elements in the list all contain the same value (in this case, the final value of $i: 1), since they're pointing to the current value of $i.

        Scalar assignments yield the lvalue on the left, not the value on the right. Now can you make that fit with your "interesting part" and understand why it works as it does?

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://347092]
Approved by insensate
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2023-12-06 06:56 GMT
Find Nodes?
    Voting Booth?
    What's your preferred 'use VERSION' for new CPAN modules in 2023?

    Results (29 votes). Check out past polls.