Почему x == (x = y) не совпадает с (x = y) == x? - PullRequest
0 голосов
/ 12 декабря 2018

Рассмотрим следующий пример:

class Quirky {
    public static void main(String[] args) {
        int x = 1;
        int y = 3;

        System.out.println(x == (x = y)); // false
        x = 1; // reset
        System.out.println((x = y) == x); // true
     }
}

Я не уверен, есть ли элемент в спецификации языка Java, который диктует загрузку предыдущего значения переменной для сравнения с правой стороной (x = y) который в порядке, указанном в скобках, должен быть рассчитан первым.

Почему первое выражение оценивается как false, а второе - true?Я ожидал, что сначала будет оцениваться (x = y), а затем он сравнил бы x с самим собой (3) и вернул бы true.


Этот вопрос отличается от порядок вычисления подвыражений в Java-выражении в том смысле, что x здесь определенно не является «подвыражением».Для сравнения он должен быть загружен , а не «оценен».Вопрос специфичен для Java, и выражение x == (x = y), в отличие от надуманных непрактичных конструкций, обычно создаваемых для сложных вопросов интервью, пришло из реального проекта.Предполагалось, что это будет однострочная замена для идиомы сравнения и замены

int oldX = x;
x = y;
return oldX == y;

, которая, будучи даже более простой, чем инструкция x86 CMPXCHG, заслуживает более короткого выражения в Java.

Ответы [ 14 ]

0 голосов
/ 29 декабря 2018

Здесь речь идет о порядке приоритетов арифматических операторов / реляционных операторов из двух операторов = против ==, доминирующим из которых является == (доминируют реляционные операторы), поскольку он предшествует = операторам присваивания.Несмотря на приоритет, порядок оценки - LTR (СЛЕВА ПРАВО), приоритет проявляется после порядка оценки.Таким образом, независимо от каких-либо ограничений оценка LTR.

0 голосов
/ 18 декабря 2018

Легко во втором сравнении слева это присвоение после присвоения y x (слева), после чего вы сравниваете 3 == 3. В первом примере вы сравниваете x = 1 с новым назначением x = 3.Кажется, что текущие операторы чтения текущего состояния всегда слева направо от x.

0 голосов
/ 22 декабря 2018

== является оператором равенства сравнения и работает слева направо.

x == (x = y);

здесь старое присвоенное значение x сравнивается с новым присвоенным значением x, (1 == 3) // false

(x = y) == x;

Принимая во внимание, что здесь новое назначаемое значение x сравнивается с новым удерживающим значением x, присвоенным ему непосредственно перед сравнением, (3 == 3) // true

Теперь рассмотрим это

    System.out.println((8 + (5 * 6)) * 9);
    System.out.println(8 + (5 * 6) * 9);
    System.out.println((8 + 5) * 6 * 9);
    System.out.println((8 + (5) * 6) * 9);
    System.out.println(8 + 5 * 6 * 9);

Вывод:

342

278

702

342

278

Таким образом, скобки играют свою главную роль в арифметических выражениях, а не в выражениях сравнения.

0 голосов
/ 15 декабря 2018

Я не уверен, есть ли в спецификации языка Java элемент, который диктует загрузку предыдущего значения переменной ...

Есть.В следующий раз, когда вам непонятно, что говорится в спецификации, прочитайте спецификацию и , затем задайте вопрос, если она неясна.

... правая сторона (x = y), которая,в порядке, подразумеваемом скобками, должен быть вычислен первым.

Это утверждение неверно. Скобки не подразумевают порядок оценки .В Java порядок вычисления слева направо, независимо от круглых скобок.Скобки определяют, где находятся границы подвыражения, а не порядок вычисления.

Почему первое выражение оценивается как ложное, а второе - как истинное?

Правилодля оператора ==: вычисление левой стороны для получения значения, вычисление правой стороны для получения значения, сравнение значений, сравнение - это значение выражения.

Другими словами,значение expr1 == expr2 всегда такое же, как если бы вы написали temp1 = expr1; temp2 = expr2;, а затем вычислили temp1 == temp2.

Правило для оператора = с локальной переменной слева: вычислитьлевая сторона для создания переменной, оценка правой стороны для получения значения, выполнение присваивания, результатом является значение, которое было присвоено.

Итак, сложите это вместе:

x == (x = y)

Мыесть оператор сравнения.Оцените левую сторону, чтобы получить значение - мы получаем текущее значение x.Оцените правую сторону: это назначение, поэтому мы оцениваем левую сторону для получения переменной - переменной x - мы оцениваем правую сторону - текущее значение y - присваиваем ей x,и результатом является присвоенное значение.Затем мы сравниваем исходное значение x со значением, которое было присвоено.

Вы можете выполнить (x = y) == x в качестве упражнения.Опять же, помните, все правила оценки левой стороны выполняются до того, как все правила оценки правой стороны .

Я ожидал (x = y), что будет оцениватьсясначала, а затем он сравнил бы x с самим собой (3) и вернул бы true.

Ваше ожидание основано на ряде неверных представлений о правилах Java.Надеюсь, теперь у вас есть правильные убеждения, и в будущем вы ожидаете истинных вещей.

Этот вопрос отличается от «порядка вычисления подвыражений в выражении Java»

Этоутверждение неверноЭтот вопрос совершенно уместен.

x здесь определенно не является «подвыражением».

Это утверждение также неверно.Это подвыражение дважды в каждом примере.

Его нужно загружать для сравнения, а не «оценивать».

Понятия не имею, что это значит.

Очевидно, у вас все еще много ложных убеждений.Мой совет, чтобы вы читали спецификацию, пока ваши ложные убеждения не будут заменены истинными убеждениями.

Вопрос специфичен для Java, а выражение x == (x = y), в отличие от надуманного, непрактичногоконструкции, обычно создаваемые для сложных вопросов интервью, взятые из реального проекта.

Происхождение выражения не имеет отношения к вопросу.Правила для таких выражений четко описаны в спецификации;прочитайте это!

Предполагалось, что это будет замена одной строки для идиомы сравнения и замены

Поскольку эта замена одной строки вызвала многоПутаница в вас, читатель кода, я бы предположил, что это был неудачный выбор.Делать код более кратким, но более сложным для понимания - не победа.Вряд ли код станет быстрее.

Кстати, в C # сравнивается и заменяется как библиотечный метод, который может быть привязан к машинной инструкции.Я считаю, что в Java такого метода нет, так как он не может быть представлен в системе типов Java.

0 голосов
/ 15 декабря 2018

Вопрос, который вы задали, является очень хорошим вопросом, если вы хотите написать компилятор Java или протестировать программы для проверки правильности работы компилятора Java.В Java эти два выражения должны давать результаты, которые вы видели.Например, в C ++ им это не нужно - поэтому, если кто-то повторно использует части компилятора C ++ в их компиляторе Java, вы можете теоретически обнаружить, что компилятор ведет себя не так, как должен.

Как разработчик программного обеспечения, пишущий код, который будет читабелен, понятен и понятен, обе версии вашего кода будут считаться ужасными.Чтобы понять, что делает код, нужно точно знать , как определяется язык Java.Кто-то, кто пишет код на Java и C ++, вздрогнул бы, глядя на код.Если вам нужно спросить, почему одна строка кода делает то, что делает, то вам следует избегать этого кода.(Я полагаю и надеюсь, что парни, которые правильно ответили на ваш вопрос «почему», сами также избежат этого кода).

0 голосов
/ 14 декабря 2018

Рассмотрим другой, возможно, более простой пример:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

Здесь оператор предварительного увеличения в ++x должен применяться перед сравнением - точно так же, как (x = y) в вашем примере необходимо вычислить перед сравнением.

Однако вычисление выражения по-прежнему происходит влево → вправо , поэтому первое сравнение на самом деле 1 == 2в то время как второй 2 == 2.
То же самое происходит в вашем примере.

0 голосов
/ 14 декабря 2018

Выражения оцениваются слева направо.В этом случае:

int x = 1;
int y = 3;

x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false

(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true
0 голосов
/ 13 декабря 2018

Это не то же самое.Левая часть всегда будет оцениваться перед правой, и в скобках указывается не порядок выполнения, а группировка команд.

С:

      x == (x = y)

Youв основном делают то же самое, что:

      x == y

И x будет иметь значение y после сравнения.

Пока с:

      (x = y) == x

Вы в основном делаете то же самое, что и:

      x == x

После x принял значение y .И он всегда вернет true .

0 голосов
/ 14 декабря 2018

По сути, первое утверждение x имеет значение 1, поэтому Java сравнивает 1 == с новой переменной x, которая не будет такой же

Во втором вы сказали x = y, что означает значение xизменилось и поэтому при повторном вызове это будет то же значение, следовательно, почему это правда и х == х

0 голосов
/ 13 декабря 2018

Это связано с приоритетом операторов и тем, как оцениваются операторы.

Скобки '()' имеют более высокий приоритет и имеют ассоциативность слева направо.Равенство '==' идет следующим в этом вопросе и имеет ассоциативность слева направо.Задание '=' идет последним и имеет ассоциативность справа налево.

Система использует стек для оценки выражения.Выражение оценивается слева направо.

Теперь перейдем к исходному вопросу:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false

Сначала x (1) будет помещен в стек.тогда внутренняя (x = y) будет оценена и помещена в стек со значением x (3).Теперь x (1) будет сравниваться с x (3), поэтому результат равен false.

x = 1; // reset
System.out.println((x = y) == x); // true

Здесь (x = y) будет оцениваться, теперь значение x станет 3, а x (3) будеттолкнул в стек.Теперь x (3) с измененным значением после равенства будет помещено в стек.Теперь выражение будет оценено, и оба будут одинаковыми, поэтому результат верен.

...