Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 931
Chas. Owens Avatar asked Nov 05 '09 17:11

Chas. Owens


4 Answers

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
like image 148
Chas. Owens Avatar answered Sep 28 '22 13:09

Chas. Owens


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.

like image 38
Ether Avatar answered Sep 28 '22 14:09

Ether


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

like image 20
mob Avatar answered Sep 28 '22 14:09

mob


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.

like image 39
Ven'Tatsu Avatar answered Sep 28 '22 15:09

Ven'Tatsu