Ответ 1
Помните, что a += x
действительно означает a = a + x
. Ключевой момент для понимания является то, что добавление вычисляется слева направо - то есть, a
, в a + x
вычисляются до x
.
Поэтому давайте выясним, что такое b = (a += (a += a))
. Сначала мы используем правило a += x
означает a = a + x
, и затем мы начинаем тщательно оценивать выражение в правильном порядке:
-
b = (a = a + (a = a + a))
так какa += x
означаетa = a + x
-
b = (a = 1 + (a = a + a))
посколькуa
в настоящее время1
. Помните, что мы вычисляем левый членa
перед правым членом(a = a + a)
-
b = (a = 1 + (a = 1 + a))
посколькуa
все еще1
-
b = (a = 1 + (a = 1 + 1))
посколькуa
все еще1
-
b = (a = 1 + (a = 2))
поскольку1 + 1
равно2
-
b = (a = 1 + 2)
так какa
теперь2
-
b = (a = 3)
поскольку1 + 2
равно3
-
b = 3
потомуa
теперь3
Это оставляет нас с a = 3
и b = 3
как было указано выше.
Попробуем это с другим выражением: b = (a += a) + (a += a)
:
-
b = (a = a + a) + (a = a + a)
-
b = (a = 1 + 1) + (a = a + a)
, помните, что мы оцениваем левый член перед правым -
b = (a = 2) + (a = a + a)
-
b = 2 + (a = a + a)
иa
теперь 2. Начните оценивать правый член -
b = 2 + (a = 2 + 2)
-
b = 2 + (a = 4)
-
b = 2 + 4
и теперьa
4
-
b = 6
Это оставляет нас с a = 4
и b = 6
. Это можно проверить, распечатав как a
и b
в Java/JavaScript (оба имеют такое же поведение здесь).
Это может также помочь думать об этих выражениях как деревья разбора. Когда мы оцениваем a + (b + c)
, LHS a
оценивается до RHS (b + c)
. Это закодировано в древовидной структуре:
+
/ \
a +
/ \
b c
Обратите внимание, что у нас больше нет круглых скобок - порядок операций закодирован в древовидную структуру. Когда мы оцениваем узлы в дереве, мы обрабатываем дочерние узлы в фиксированном порядке (т.е. Слева направо для +
). Например, когда мы обрабатываем корневой узел +
, мы оцениваем левое поддерево a
перед правым поддеревом (b + c)
, независимо от того, заключено ли правое поддерево в круглых скобках или нет (поскольку круглые скобки даже не присутствуют в дерево разбора).
Из-за этого Java/JavaScript не всегда сначала оценивают "самые вложенные круглые скобки", в отличие от правил, которые можно было бы обучать арифметике.
15.7. Порядок оценки
Язык программирования Java гарантирует, что операнды операторов, по-видимому, оцениваются в определенном порядке оценки, а именно слева направо.
...15.7.1. Сначала оценить операнд левой руки
Левый операнд двоичного оператора оказывается полностью оцененным до того, как будет оценена любая часть правого операнда.
Если оператор является оператором составного присваивания (§15.26.2), то оценка левого операнда включает в себя как запоминание переменной, которую левый операнд обозначает, так и выборку и сохранение этого значения переменной для использования в подразумеваемой двоичной операции,
Другие примеры, похожие на ваш вопрос, можно найти в связанной части JLS, например:
Пример 15.7.1-1. Сначала оценивается левосторонний операнд
В следующей программе оператор * имеет левый операнд, который содержит назначение переменной и правый операнд, содержащий ссылку на ту же переменную. Значение, созданное ссылкой, будет отражать тот факт, что назначение произошло первым.
class Test1 { public static void main(String[] args) { int i = 2; int j = (i=3) * i; System.out.println(j); } }
Эта программа выводит результат:
9
Недопустимо для оценки оператора * производить 6 вместо 9.