Recently I came across this question: Assignment operator chain understanding.
While answering this question I started doubting my own understanding of the behavior of the addition assignment operator +=
or any other operator=
(&=
, *=
, /=
, etc.).
My question is, when is the variable a
in the expressions below updated in place, so that its changed value is reflected in other places in the expression during evaluation, and what is the logic behind it? Please take a look at following two expressions:
Expression 1
a = 1
b = (a += (a += a))
//b = 3 is the result, but if a were updated in place then it should've been 4
Expression 2
a = 1
b = (a += a) + (a += a)
//b = 6 is the result, but if a is not updated in place then it should've been 4
In the first expression, when the innermost expression (a += a)
is evaluated, it seems that it doesn't update value of a
, thus the result comes out as 3
instead of 4
.
However, in the second expression, the value of a
is updated and so the result is 6.
When should we assume that a
's value will be reflected in other places in the expression and when should we not?
Following are the rules that need to be taken care of
expression evaluation
Expression 1
Expression 2
Expression 3
Remember that
a += x
really meansa = a + x
. The key point to understand is that addition is evaluated from left to right -- that is, thea
ina + x
is evaluated beforex
.So let's figure out what
b = (a += (a += a))
does. First we use the rulea += x
meansa = a + x
, and then we start evaluating the expression carefully in the correct order:b = (a = a + (a = a + a))
becausea += x
meansa = a + x
b = (a = 1 + (a = a + a))
becausea
is currently1
. Remember we evaluate the left terma
before the right term(a = a + a)
b = (a = 1 + (a = 1 + a))
becausea
is still1
b = (a = 1 + (a = 1 + 1))
becausea
is still1
b = (a = 1 + (a = 2))
because1 + 1
is2
b = (a = 1 + 2)
becausea
is now2
b = (a = 3)
because1 + 2
is3
b = 3
becausea
is now3
This leaves us with
a = 3
andb = 3
as reasoned above.Let's try this with the other expression,
b = (a += a) + (a += a)
:b = (a = a + a) + (a = a + a)
b = (a = 1 + 1) + (a = a + a)
, remember we evaluate the left term before the right oneb = (a = 2) + (a = a + a)
b = 2 + (a = a + a)
anda
is now 2. Start evaluating the right termb = 2 + (a = 2 + 2)
b = 2 + (a = 4)
b = 2 + 4
anda
is now4
b = 6
This leaves us with
a = 4
andb = 6
. This can be verified by printing out botha
andb
in Java/JavaScript (both have the same behavior here).It might also help to think of these expressions as parse trees. When we evaluate
a + (b + c)
, the LHSa
is evaluated before the RHS(b + c)
. This is encoded in the tree structure:Note that we don't have any parentheses anymore -- the order of operations is encoded into the tree structure. When we evaluate the nodes in the tree, we process the node's children in a fixed order (i.e., left-to-right for
+
). For instance, when we process the root node+
, we evaluate the left subtreea
before the right subtree(b + c)
, regardless of whether the right subtree is enclosed in parentheses or not (since the parentheses aren't even present in the parse tree).Because of this, Java/JavaScript do not always evaluate the "most nested parentheses" first, in contrast to rules you might have been taught for arithmetic.
See the Java Language Specification:
More examples similar to your question can be found in the linked part of the JLS, such as:
It just uses a variation of order of ops.
If you need a reminder of order of ops:
This variation just is read left to right, but if you see a parenthesis do everything inside it, and replace it with a constant then move on.
First ex:
var b = (a+=(a+=a))
var b = (1+=(1+=1))
var b = (1+=2)
var b = 3
Second ex:
var b = (a+=a)+(a+=a)
var b = (1+=1)+(a+=a)
var b = 2 + (2+=2)
var b = 2 + 4
var b = 6
The last one
b = a += a += a
since there are no parenthesis, it automatically becomesb = 1 += 1 += 1
which isb = 3