Addition assignment += поведение в выражении
недавно я наткнулся на этот вопрос: понимание цепочки операторов назначения.
отвечая на этот вопрос, я начал сомневаться в своем понимании поведения оператора сложения задание +=
или любое другое operator=
(&=
, *=
, /=
, etc.).
мой вопрос в том, когда переменная a
в приведенных ниже выражениях обновляется на месте, так что его измененное значение отражается в других местах в выражение во время оценки, и какова логика за этим? Пожалуйста, взгляните на следующие два выражения:
выражение 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
выражения 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
в первом выражении, когда внутреннее выражение (a += a)
оценивается, кажется, что он не обновляет значение a
, таким образом результат выходит как 3
вместо 4
.
однако во втором выражении значение a
обновляется и поэтому результат 6.
когда мы должны считать, что a
значение будет отражаться в других местах в выражении, и когда мы не должны?
3 ответов
помните, что 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))
, потому что и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
is2
-
b = (a = 1 + 2)
, потому чтоa
теперь2
-
b = (a = 3)
, потому что1 + 2
is3
-
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 do не всегда оценивайте "самые вложенные круглые скобки" сначала, в отличие от правил, которым вас могли бы научить для арифметики.
посмотреть Спецификация Языка Java:
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.
Ниже приведены правила, о которых необходимо позаботиться
- приоритет операторов
- переменной задание
-
выражения
выражение 1
a = 1 b = (a += (a += a)) b = (1 += (a += a)) // a = 1 b = (1 += (1 += a)) // a = 1 b = (1 += (1 += 1)) // a = 1 b = (1 += (2)) // a = 2 (here assignment is -> a = 1 + 1) b = (3) // a = 3 (here assignment is -> a = 1 + 2)
выражения 2
a = 1 b = (a += a) + (a += a) b = (1 += a) + (a += a) // a = 1 b = (1 += 1) + (a += a) // a = 1 b = (2) + (a += a) // a = 2 (here assignment is -> a = 1 + 1) b = (2) + (2 += a) // a = 2 (here here a = 2) b = (2) + (2 += 2) // a = 2 b = (2) + (4) // a = 4 (here assignment is -> a = 2 + 2) b = 6 // a = 4
выражение 3
a = 1 b = a += a += a += a += a b = 1 += 1 += 1 += 1 += 1 // a = 1 b = 1 += 1 += 1 += 2 // a = 2 (here assignment is -> a = 1 + 1) b = 1 += 1 += 3 // a = 3 (here assignment is -> a = 1 + 2) b = 1 += 4 // a = 4 (here assignment is -> a = 1 + 3) b = 5 // a = 5 (here assignment is -> a = 1 + 4)
он просто использует изменение порядка операций.
Если вам нужно напоминание о порядке ops:
PEMDAS:
P = скобки
E = показатели
MD = Умножение/Деление
AS = Сложение/Вычитание
остальные слева направо.
этот вариант просто читайте слева направо, но если вы видите, что скобки делают все внутри него и заменяют его константой, то двигайтесь дальше.
первый пример:
var b = (a+=(a+=a))
var b = (1+=(1+=1))
var b = (1+=2)
var b = 3
второй пример:
var b = (a+=a)+(a+=a)
var b = (1+=1)+(a+=a)
var b = 2 + (2+=2)
var b = 2 + 4
var b = 6
var a = 1
var b = (a += (a += a))
console.log(b);
a = 1
b = (a += a) + (a += a)
console.log(b);
a = 1
b = a += a += a;
console.log(b);
последние один b = a += a += a
поскольку нет скобок, он автоматически становится b = 1 += 1 += 1
что это b = 3