Несколько назначений на одну строку не работают как ожидалось
Я пытаюсь поменять местами два int
- x
и y
в примере и сделать это в одной строке без библиотечной функции.
Итак, я начал с этого:
int x = 4;
int y = 3;
System.out.println(x);
System.out.println(y);
x ^= y;
System.out.println(x);
System.out.println(y);
y ^= x;
System.out.println(x);
System.out.println(y);
x ^= y;
System.out.println(x);
System.out.println(y);
Вывод был 4, 3, 7, 3, 7, 4, 3, 4
, как ожидалось. Все хорошо до сих пор.
Далее было следующее:
int x = 4;
int y = 3;
System.out.println(x);
System.out.println(y);
y ^= (x ^= y);
System.out.println(x);
System.out.println(y);
x ^= y;
System.out.println(x);
System.out.println(y);
Результат был 4, 3, 7, 4, 3, 4
, как и ожидалось еще раз. До сих пор хорошо.
Тогда, наконец, это:
int x = 4;
int y = 3;
System.out.println(x);
System.out.println(y);
x ^= (y ^= (x ^= y));
System.out.println(x);
System.out.println(y);
На этом этапе выход стал 4, 3, 0, 4
. Теперь я знаю, что 0
является результатом 4 ^ 4
, потому что назначение x
в то время не было полным - почему это происходит? Почему x ^= y
не присваивает 7
переменной x
, чтобы она стала 7 ^ 4
для последнего назначения?
Ответы
Ответ 1
Попробуйте расширить ваше последнее выражение.
Он оценивает,
x = x^(y = y^ (x = x^y));
Обратите внимание, что выражения оцениваются от слева до справа,
он становится,
x = 4 ^ (y = 3 ^ (x = 4 ^ 3));
Теперь проблема стала очевидной. Правильно?
Изменить:
Чтобы устранить путаницу, позвольте мне попытаться объяснить, что я имею в виду, оценивая слева направо.
int i = 1;
s = i + (i = 2) + i;
Теперь выражение будет оценивать,
s = 1 + 2 + 2;
Обратите внимание, что i
в левом присваивания было 1
, но при назначении справа (и в настройке) было оценено значение 2
, потому что оценка выполняется слева направо, когда она попадает во вторую и третью части выражения, значение i
было 2
.
Ответ 2
Порядок оценки определяется в главе 15 JLS. Элемент 15.7.1 говорит:
Если оператор является оператором составного присваивания (§15.26.2), то оценка левого операнда включает в себя как запоминание переменной, которую левый операнд обозначает, так и выборку и сохранение этого значения переменной для использования в подразумеваемая двоичная операция.
Чтобы объяснить далее, у них есть два примера вычислений, которые включают в себя назначения. Здесь задание находится слева от оператора:
int i = 2;
int j = (i=3) * i;
System.out.println(j);
И они специально говорят, что результат равен 9, и ему не разрешено быть 6. То есть (i = 3) вычисляется как 3, а я присваивается 3 перед умножением на себя.
Но во втором примере:
int a = 9;
a += (a = 3); // first example
System.out.println(a);
int b = 9;
b = b + (b = 3); // second example
System.out.println(b);
JLS указывает, что обе распечатки должны выдавать 12, и не разрешено создавать 6. То есть, поскольку назначение b находится с правой стороны, значение слева b
(или неявное левое a
в операции +=
), значение перед этим присваиванием выбирается и сохраняется сначала, и только тогда выполняется операция внутри круглых скобок.
Внутри выражения разбиваются на операции JVM, которые вызывают и вытесняют значения в "стек операнда". Если вы так думаете об этом - это в b = b + (b=3)
Значение b
сначала помещается в стек операнда, затем выполняется (b = 3), и его значение затем добавляется к значению, выбитому из стека ( старое значение b), это будет иметь смысл. В этот момент левая рука b
просто означает "Что такое значение b, когда оно было нажато в стеке", а не "текущее значение b".
Ответ 3
Позволяет делить и вычислять.
Самая внутренняя скобка выполняется первой, и после того, как все скобки разрешены, выражение выполняется слева направо.
x ^= (y ^= (x ^= y)); // initial statement
x = x^(y = y^ (x = x^y)); //equals to
() have the highest precedence
x = x^(y = y^ (x = 3^4)); // first highest precedence ()
x = x^(y = y ^ (x = 7)); // still the first x is 3
x = 4 ^(y = 3 ^ (x = 7)); // now 3 ^ 7 =4
x = 4 ^ 4; // now 3 ^ 7 =4
x= 0;