Порядок операций с логикой в ​​Java Confused

У меня есть проблема с порядком операций из викторины, и объяснение не совсем полезно. Вот код:

package com.udayan.oca;

public class Test {
     public static void main(String [] args) {
         int a = 2;
         boolean res = false;
         res = a++ == 2 || --a == 2 && --a == 2;
         System.out.println(a);
     }
}

Он говорит, что печатает 3, что он и делает, потому что я проверял это, но не понимаю, как это сделать. Вот их объяснение:

a++ == 2 || --a == 2 && --a == 2;

[Данное выражение]. (a++) == 2 || --a == 2 && --a == 2;

[Postfix имеет более высокий приоритет, чем другие операторы].

(a++) == 2 || (--a) == 2 && (--a) == 2;

[После постфикса приоритет отдается префиксу].

((a++) == 2) || ((--a) == 2) && ((--a) == 2);

[== имеет более высокий приоритет, чем && и ||].

((a++) == 2) || (((--a) == 2) && ((--a) == 2));

[&& имеет более высокий приоритет, чем ||].

Приступим к ее решению: ((a++) == 2) || (((--a) == 2) && ((--a) == 2));

[а=2, рез=ложь].

(2 == 2) || (((--a) == 2) && ((--a) == 2));

[а=3, рез=ложь]. true || (((--a) == 2) && ((--a) == 2));

[а=3, рез=ложь].

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

res истинно, а a равно 3.

Да, я понимаю короткое замыкание, так что нет необходимости объяснять это.

Итак, вот мое мнение, однако:

res = a++ == 2 || --a == 2 && --a == 2 ->
(((a++) == 2) || (((--a) == 2) && ((--a) == 2))) [a = 2]

(((a++) == 2) || ((**1** == 2) && ((--a) == 2))) [a = 1]

(((a++) == 2) || (**false** && (**0** == 2))) [a = 1] //short-circuits

(((a++) == 2) || **false**) [a = 1] //short circuits

(**false**) [a = 1]

???? Другой момент заключается в том, что ключ ответа говорит сначала сделать ++, а затем || следующий. ++ да, это имеет смысл. Но я думал, что && стоит перед ||.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
384
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий
res = a++ == 2 || --a == 2 && --a == 2 (res is true)

1. a++ (post-increment, no) -> a = a + 1 -> it's still 2 -> when true -> it becomes 3
2. --a (pre-increment, right to left) -> a - 1 = a -> 1
3. --a (pre-increment, right to left) -> a - 1 = a -> 0 (its because of logical and, it never execute this part) 
4. == (equality, left to right) -> 2 == 2 || 1 == 2 && 0 == 2 -> true || false && false
5. && (logical and, left to right) -> false -> no more steps 
6. || (logical or, left to right) -> true -> go to 1.

// so take 3
// moral of the story is always use paranthesis
// op is correct for short-circuit 

Но скобки приведут к тому, что я сделаю это... и получу 0?

gmanrocks 13.03.2019 04:31

как ты получил 0? это невозможно

phonemyatt 13.03.2019 04:35

шаг 3 дает a = 0. a больше не изменяется в 4 5 и 6

gmanrocks 13.03.2019 04:40

ладно, кажется, я знаю, почему ты все еще в замешательстве. приоритет похож на группировку. поэтому он будет выполняться шаг за шагом. почему бы вам не попробовать перевернуть уравнение и выполнить шаги, как я, и объяснить мне, почему оно получает 2. res = --a == 2 && --a == 2 || а++ == 2; (не забывайте следить за операторским уровнем и ассоциативностью, тогда вы поймете, о чем короткое замыкание)

phonemyatt 13.03.2019 04:55

1. а++ -> --а == 2 && --а == 2 || 2 == 2 где a = 3 //разрыв строки// 2. --a -> 1 == 2 && --a == 2 || 2 == 2 где a = 1 //разрыв строки// 3. 1 == 2 && 0 == 2 || 2 == 2 где a = 0 // разрыв строки false && false || правда -> ложь

gmanrocks 13.03.2019 05:02

нет, на самом деле вы получите 2 из-за a++ -> --a (только один раз) -> поэтому 2

phonemyatt 13.03.2019 05:02

Итак, на шаге 2 обратной задачи оценивается 1 == 2 -> false, а затем путается с || а так как он ложный && замыкает и пропускает другую сторону?

gmanrocks 13.03.2019 05:06

В конце концов, когда (((a++) == 2) || ложный) [a = 1] Делается тогда как || оператор имеет меньший приоритет, чем ++, поэтому здесь a станет 3 .. тогда он напечатает a=3 Хотя это оператор короткого замыкания, он все равно должен выполнять оператор ++ 1st.

Из Спецификация языка Java,

The conditional-or operator || operator is like | (§15.22.2), but evaluates its right-hand operand only if the value of its left-hand operand is false.

Итак, это проще, чем вы думаете. res = a++ == 2 || --a == 2 && --a == 2; оценивается так:

res = ((a++ == 2) || ((--a == 2) && (--a == 2)));

a++ == 2? Постинкремент означает, что а читается как 2. Затем это выражение оценивается. 2 == 2, это правда. Короткое замыкание означает, что остальная часть выражения оценивается как никогда.

Итак, по сути, весь приведенный выше код — это res = a++ == 2;

Я сделал простую программу, чтобы проверить это:

public class TestSOCode {
    public static void main(String [] args) {
        test1();
    }

    private static void test1(){
        int a = 2;
        boolean res = false;
        //res = a++ == 2 || --a == 2 && --a == 2;
        res = expression(a++, "One") || expression(--a, "Two") && expression(--a, "Three");
        System.out.println(res +" "+ a);
    }

    private static boolean expression(int i, String s){
        System.out.println(s+ " called with "+ i);
        return i == 2;
    }
}

Это дает результат

One called with 2
true 3

ОБНОВИТЬ: После некоторого обсуждения и исследования я думаю, что возникло неправильное понимание разницы между приоритетом и порядком выполнения, когда речь идет о логических операторах.

res = a++ == 2 || --a == 2 && --a == 2;

Приоритет приведенного выше оператора вычисляется до, когда он оценивается. Я не буду рассматривать другие правила приоритета, так как это усложнит этот ответ, поэтому я упрощу его:

res = x || y && z;

&& имеет приоритет, поэтому выражения группируются следующим образом:

res = x || (y && z);

Как мы видим, && имеет приоритет, поэтому выражения слева и справа от него группируются вместе, затем вычисляется ||. Выражение слева от него — x, а выражение справа — (y && z) (я думаю, мы оба думали, что если && имеет приоритет, оно будет похоже на (a || b) && c, поэтому оно будет оцениваться первым, но это не так). Если мы хотим убедиться, что это действительно так, мы можем изменить приведенный выше код следующим образом:

 res = expression(a = 8, "One") || expression(a = 16, "Two") && expression(a = 32, "Three");

Это эквивалентно false || (false && false), но без какого-либо вмешательства компилятора в константы времени компиляции. Результатом этого является:

One called with 8
Two called with 16
false 16

Сначала оценивается ||, затем левая часть &&. Это возвращает false, а false && ? всегда будет false, поэтому третье выражение не оценивается. Но правила приоритета не были нарушены. Я надеюсь, что это прояснило любую путаницу. Если нет, я буду рад продолжить обсуждение в чате и обновить свой ответ. Поскольку из исходного кода мы знаем, что если первое выражение истинно, || возвращает истину и замыкается, мы можем сказать, что a || b && c не сгруппировано в (a || b) && c.

но да, это будет короткое замыкание, если || имел более высокий приоритет... разве && не имеет более высокий приоритет? Итак, прежде чем это произойдет || к короткому замыканию, он должен иметь дело с &&?

gmanrocks 13.03.2019 05:04

Нет. Сначала оценивается левая сторона ||. Я добавлю тестовый код в свой ответ

Liam 13.03.2019 05:05

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

gmanrocks 13.03.2019 05:07

Итак, я подумал, что, возможно, компилятор оптимизирует код, так как он видит, что a равно 2 во время компиляции. Я попытался использовать Math.random(), чтобы установить a на что-то, что компилятор не может увидеть во время компиляции, но если это случайное число было 2, ответ был таким же. Мой единственный вывод заключается в том, что он читает || первым, несмотря на их явно более высокий приоритет.

Liam 13.03.2019 05:41

Oy vey, почему это не соблюдает порядок старшинства. Это не может быть глюк. Должна быть какая-то глупая причина...

gmanrocks 13.03.2019 05:46

Я забыл упомянуть, что когда я использовал Math.random(), я использовал ваш исходный код, а не приведенный выше код. Просто чтобы исключить любую другую фанковость. Это странно, хорошо ... я буду продолжать смотреть вокруг

Liam 13.03.2019 05:48

Я попробовал логическое значение bar = foo(true, 1) || foo(false, 2) && foo(true, 3); где foo — это статический логический метод, который печатает второй аргумент и возвращает первый аргумент. Все, что он сделал, это напечатал 1. Это означает, что он работает правильно || foo(false, 2) && foo(true, 3) и короткое замыкание, не заморачиваясь с &&, как если бы || и && просто идут слева направо. foo(истина, 3) && foo(истина, 1) || foo(false, 2) -> 3 1. Таким образом, в любом порядке кажется, что он идет слева направо.

gmanrocks 13.03.2019 05:53

Давайте продолжить обсуждение в чате.

Liam 13.03.2019 06:03

Другие вопросы по теме