Search code examples
perlvariable-assignmentparentheses

Need help understanding how Perl 5 is parsing a compound assignment statement referencing same variable


I need to understand how this simple expression is being evaluated, since the result is not as I expected.

I'm quite new to Perl, but thought I had enough understanding to explain the outcome of this seemingly straightforward snippet. Clearly I'm missing something. I've used Deparse to see how Perl is processing the expression, and Deparse does not change the parentheses I already had in place.

$i = 12;
$i = (($i /= 2) + ($i = 100));
print $i;

According to my understanding, the result should be 106, assuming the expression is evaluated in the order indicated by parentheses, and in the manner it seems it should be. I would think: $i is first divided by 2, thereby assigning 6 to $i and resulting in a value of 6. Then 100 is assigned to $i, and 100 is the result of that second expression. 6 + 100 = 106, which I would think would finally be assigned to $i. Instead, it prints "200" .

In PHP, the same code indeed yields "106", leading me to believe that this has to do with some part of the expression being interpreted as a list, or something equally Perl-wonderful. Can't wait to find out what I got wrong.


Solution

  • The perl-wonderful thing is that arguments (whether lvalues or rvalues) are always passed to perl operators as references to the actual variables, not as copies of their values. This is different from most other languages, and is keeping with the fact that perl is a pass-by-reference language (like Fortran).

    Your example is an very unfortunate red-herring, since it assumes that the operands of + are evaluated left-to-right, which (while absolutely true for the only usable perl5 implementation) is afaik not guaranteed by any docs.

    Let's try it with a comma operator, which really is[1] guaranteed to evaluate its arguments left-to-right:

    perl -le 'print @y = ($x = 1, $x = 2, $x = 3)'
    

    That should print 123 right?

    No, because perl will first evaluate all the assignments from left to right, each of which returning $x, not a copy of it, and then will proceed 3 times to "resolve" it by derefencing it, getting each time in the last value that was stored in it. Thence 333.

    [1]: from perlop(1): "Comma operator ... In list context, it's just the list argument separator, and inserts both its arguments into the list. These arguments are also evaluated from left to right".

    [the extra assignment above in order to avoid a discussion about how and why in perl an argument list is actually a list, built with the comma-operator, not something special as in C]