views:

193

answers:

4

Given the code:

my $x = 1;

$x = $x * 5 * ($x += 5);

I would expect $x to be 180:

$x = $x * 5 * ($x += 5); #$x = 1
$x = $x * 5 * 6;         #$x = 6
$x = 30 * 6;
$x = 180;
180;

But instead it is 30; however, if I change the ordering of the terms:

$x = ($x += 5) * $x * 5;

I do get 180. The reason I am confused is that perldoc perlop says very plainly:

A TERM has the highest precedence in Perl. They include variables, quote and quote-like operators, any expression in parentheses, and any function whose arguments are parenthesized.

Since ($x += 5) is in parentheses, it should be a term, and therefore executed first, regardless of the ordering of the expression.

+15  A: 

The act of typing out the question yielded the answer to me: terms have the highest precedence. That means that the $x in the first chunk of code is evaluated and yields 1, then 5 is evaluated and yields 5, then ($x += 5) is evaluate and yields 6 (with a side-effect of setting $x to 6):

$x = $x * 5 * ($x += 5);
address of $x = $x * 5 * ($x += 5); #evaluate $x as an lvalue
address of $x = 1 * 5 * ($x += 5);  #evaluate $x as an rvalue
address of $x = 1 * 5 * ($x += 5);  #evaluate 5
address of $x = 1 * 5 * 6;          #evaluate ($x += 5), $x is now 6
address of $x = 1 * 5 * 6;          #evaluate 1 * 5
address of $x = 5 * 6;              #evaluate 1 * 5
address of $x = 30;                 #evaluate 5 * 6
30;                                 #evaluate address of $x = 30

Similarly, the second example reduces like this:

$x = ($x += 5) * $x * 5; 
address of $x = ($x += 5) * $x * 5; #evaluate $x as an lvalue
address of $x = 6 * $x * 5;         #evaluate ($x += 5), $x is now 6
address of $x = 6 * 6 * 5;          #evaluate $x as an rvalue
address of $x = 6 * 6 * 5;          #evaluate 5
address of $x = 36 * 5;             #evaluate 6 * 6
address of $x = 180;                #evaluate 36 * 5
180;                                #evaluate $x = 180
Chas. Owens
Correct. What was tripping you up wasn't precedence but order of evaluation. The `($x += 5)` isn't evaluated until after the first multiplication takes place. The parentheses only serve to ensure that the `+=` happens before the (second) multiplication. In this case, that prevents a syntax error because multiplication has a higher precedence than assignment and the result of multiplication isn't a valid lvalue.
Michael Carman
+4  A: 

$x by itself is also a TERM. Since it is encountered first (in your first example), it is evaluated first.

mobrule
+8  A: 

Whenever I have confusion about stuff like this I first pull out perldoc perlop, and then if I'm still not sure, or want to see how a particular block of code will get executed, I use B::Deparse:

perl -MO=Deparse,-p,-q,-sC
my $x = 1;
$x = $x * 5 * ($x += 5);

^D

gives:

(my $x = 1);
($x = (($x * 5) * ($x += 5)));
- syntax OK

So substituting values at each stage gives:

($x = (($x * 5) * ($x += 5)));
($x = ((1 * 5) * ($x += 5)));
($x = ((5) * (6))); # and side-effect: $x is now 6
($x = (5 * 6));
($x = (30));
($x = 30);
$x = 30;

So the fact that $x was temporarily set to 6 doesn't really affect anything, because the earlier value (1) was already substituted into the expression, and by the end of the expression it is now 30.

Ether
Interesting. But it brings up the question where the parens around $x*5 come from.
innaM
`*` is a binary operator that evaluates left-to-right, so `$x * $y * $z` would evaluate as `(($x * $y) * $z)`.
Ether
Ether, *evaluating* left to right isn't the same as *associating* left to right. Even if `*` associated right to left, it could still be evaluated left to right: first evaluate $x and store its value in a temporary; then evaluate $y, then $z; next multiply them and multiply the result with the saved temporary value from $x. Precedence doesn't determine evaluation order, unless you can find some documentation that says otherwise for Perl. (Java, for instance, is defined to evaluate from left to right, even if a right-hand operator has higher precedence.)
Rob Kennedy
(What I mean to say is that Java evaluates *operands* from left to right. *Operators* are evaluated according to their precedence rules.)
Rob Kennedy
@Rob: yes, you're absolutely right. Thanks for correcting my muddy wording! Clarity of thought means clarity of code.. :)
Ether
+2  A: 

The associativity of the * operator is leftward, so the left most term is always evaluated before the right most term. Other operators, such as ** are right associative and would have evaluated ($x += 5) before the rest of the statement.

Ven'Tatsu
You're conflating associativity, precedence, and evaluation order. They're three separate things.
Rob Kennedy