How does Perl decide which order to evaluate terms in an expression?
Asked Answered
H

4

5

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.

Hypercorrect answered 5/11, 2009 at 17:46 Comment(5)
You know, having learned C first, I never do stuff like this and expect it to work the way I think it ought to work: c-faq.com/expr/index.html ;-)Infinitude
I too came from ANSI C, and yeah, this isn't code I would write, it is me trying to make sure I understand precedence in Perl before I explain it to someone else. Using side-effects like this is a major no-no, but still legal in Perl. In ANSI C, if you had more than one side-effect in an expression the results were undefined, in Perl side-effects are better defined, but still a really bad idea.Hypercorrect
I once made a lovely post about this to comp.lang.perl.misc, and have never been able to find it again.Partial
My advice: don't do this. Perl allows it, but it's a serious readability problem. You do not want to have to work on code where you have to carefully parse an expression to figure out what it means.Farly
@David Thornley It isn't a matter of doing it. It is a matter of understanding it. I am writing an article that involves precedence and associativity of operators and I don't want to state anything that is false. This means I need to understand what Perl does much better than is normally necessary; hence the bad, but legal, code as I look for inconsistencies in my understanding.Hypercorrect
H
16

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
Hypercorrect answered 5/11, 2009 at 17:46 Comment(1)
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.Deshawndesi
A
10

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.

Agglutinogen answered 5/11, 2009 at 18:17 Comment(5)
Interesting. But it brings up the question where the parens around $x*5 come from.Vorous
* is a binary operator that evaluates left-to-right, so $x * $y * $z would evaluate as (($x * $y) * $z).Agglutinogen
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.)Accompanyist
(What I mean to say is that Java evaluates operands from left to right. Operators are evaluated according to their precedence rules.)Accompanyist
@Rob: yes, you're absolutely right. Thanks for correcting my muddy wording! Clarity of thought means clarity of code.. :)Agglutinogen
A
4

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

Abutilon answered 5/11, 2009 at 18:3 Comment(0)
F
2

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.

Fishman answered 5/11, 2009 at 21:16 Comment(1)
You're conflating associativity, precedence, and evaluation order. They're three separate things.Accompanyist

© 2022 - 2024 — McMap. All rights reserved.