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 + 1is2 -
b = (a = 1 + 2), потому чтоaтеперь2 -
b = (a = 3), потому что1 + 2is3 -
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