Почему "a^=b^=a^=b" отличается от "a^=b; b^=a; A^=b;"?
я попробовал код для замены двух целых чисел на Java без использования 3-й переменной, используя XOR.
вот две функции подкачки, которые я пробовал:
package lang.numeric;
public class SwapVarsDemo {
public static void main(String[] args) {
int a = 2984;
int b = 87593;
swapDemo1(a,b);
swapDemo2(a,b);
}
private static void swapDemo1(int a, int b) {
a^=b^=a^=b;
System.out.println("After swap: "+a+","+b);
}
private static void swapDemo2(int a, int b) {
a^=b;
b^=a;
a^=b;
System.out.println("After swap: "+a+","+b);
}
}
выход производства этого кода:
After swap: 0,2984
After swap: 87593,2984
мне любопытно знать, почему это заявление:
a^=b^=a^=b;
отличается от этой?
a^=b;
b^=a;
a^=b;
6 ответов
вопрос в порядке оценки:
посмотреть JLS раздел 15.26.2
во-первых, левый операнд вычисляется для создания переменной. если эта оценка завершается резко, то выражение присвоения завершается резко по той же причине; правый операнд не оценено и назначение не происходит.
в противном случае-значение левого операнда сохраняется, и затем вычисляется правый операнд. Если эта оценка завершится резко, то выражение присвоения завершается резко по той по той же причине, и никакого назначения не происходит.
в противном случае, сохраненное значение левой переменной и значение правый операнд используется для выполнения двоичной операции указывается оператором составного назначения. Если эта операция завершается резко, то выражение присвоения завершается резко по той же причине и нет происходит назначение.
в противном случае результат двоичной операции преобразуется в тип левой переменной, подвергнутой преобразованию набора значений (§5.1.13) к соответствующему стандартному значению (не расширенному значению экспоненты set), и результат преобразования сохраняется в переменную.
таким образом, ваше выражение делает:
a^=b^=a^=b;
- оценить
a
- оценить
b^=a^=b
- xor два (так что
a
в шаге один не имеет^=b
применяется к нему еще) - сохранить результат в
a
другими словами, ваше выражение эквивалентно следующему java-коду:
int a1 = a;
int b2 = b;
int a3 = a;
a = a3 ^ b;
b = b2 ^ a;
a = a1 ^ b;
вы можете видеть, что из разобранной версии вашего метода:
private static void swapDemo1(int, int);
Code:
0: iload_0
1: iload_1
2: iload_0
3: iload_1
4: ixor
5: dup
6: istore_0
7: ixor
8: dup
9: istore_1
10: ixor
11: istore_0
, потому что a ^= b ^= a ^= b;
обрабатывается так:
a ^= (b ^= (a ^= b));
который можно уменьшить до:
a ^= (b ^= (a ^ b));
так b
будет иметь значение b ^ (a ^ b)
и наконец a
будет a ^ (b ^ (a ^ b)
.
Это очень похоже на запись в Блоха и Гафтера Java Puzzlers книга, см. Главу 2, головоломка 7 ("поменять мясо"). Я не могу улучшить его.
объяснение в решении:
эта идиома была использована в языке программирования C и оттуда сделана его путь в C++, но не гарантируется работа в любом из них языки. Он гарантированно не будет работать на Java. язык Java спецификация говорит, что операнды операторы оцениваются слева направо [JLS 15.7]. Чтобы оценить выражение
x ^= expr
, значениеx
отбирается перед оценкой expr, и исключительное или из этих двух значений присваивается переменнойx
[JLS 15.26.2]. В программе CleverSwap переменнаяx
выборка дважды-один раз для каждого внешнего вида в выражении-но оба выборки происходят перед любыми назначениями. Следующий фрагмент кода описывает поведение сломанной идиомы swap в подробнее и объясняет вывод, который мы наблюдали:
код, указанный в приведенной выше цитате:
// The actual behavior of x ^= y ^= x ^= y in Java
int tmp1 = x; // First appearance of x in the expression
int tmp2 = y; // First appearance of y
int tmp3 = x ^ y; // Compute x ^ y
x = tmp3; // Last assignment: Store x ^ y in x
y = tmp2 ^ tmp3; // 2nd assignment: Store original x value in y
x = tmp1 ^ y; // First assignment: Store 0 in x
Проверьте приоритет оператора (ref:http://introcs.cs.princeton.edu/java/11precedence/ в результате поиска приоритета оператора java). Здесь появляется ссылка на документацию Oracle (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html), хотя он немного плотный).
в частности, ^= обрабатывается справа налево (а не слева направо)
У меня недостаточно очков репутации, чтобы прокомментировать ответ Натана.
@Nathan Hughes говоря о книге Java Puzzlers находится на месте, и его ответ указывает на некоторые идеи, которые позволяют вам сделать это в 1 строке. Хотя и не так элегантно, как вопрос ОП.Как указывает Натан:
в программе CleverSwap переменная x отбирается дважды-один раз для каждого появления в выражении,но оба выборки происходят до любые задания
а также использование https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html в качестве руководства, вы можете сделать это в 1 строке с:
a = ( b = (a = a ^ b) ^ b) ^ a;
ключ, являющийся назначением значения переменной a как части первого XOR, гарантирующего, что вы сохраняете это в левой части второго XOR (см. ссылку jls выше, для хорошего примера назначения в левом операнде). Аналогично, установка переменной b с результатами второй КСОР, снова держась слева от последнего КСОРА.