У меня есть проблема с порядком операций из викторины, и объяснение не совсем полезно. Вот код:
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]
???? Другой момент заключается в том, что ключ ответа говорит сначала сделать ++, а затем || следующий. ++ да, это имеет смысл. Но я думал, что && стоит перед ||.




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? это невозможно
шаг 3 дает a = 0. a больше не изменяется в 4 5 и 6
ладно, кажется, я знаю, почему ты все еще в замешательстве. приоритет похож на группировку. поэтому он будет выполняться шаг за шагом. почему бы вам не попробовать перевернуть уравнение и выполнить шаги, как я, и объяснить мне, почему оно получает 2. res = --a == 2 && --a == 2 || а++ == 2; (не забывайте следить за операторским уровнем и ассоциативностью, тогда вы поймете, о чем короткое замыкание)
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 || правда -> ложь
нет, на самом деле вы получите 2 из-за a++ -> --a (только один раз) -> поэтому 2
Итак, на шаге 2 обратной задачи оценивается 1 == 2 -> false, а затем путается с || а так как он ложный && замыкает и пропускает другую сторону?
В конце концов, когда (((a++) == 2) || ложный) [a = 1] Делается тогда как || оператор имеет меньший приоритет, чем ++, поэтому здесь a станет 3 .. тогда он напечатает a=3 Хотя это оператор короткого замыкания, он все равно должен выполнять оператор ++ 1st.
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.
но да, это будет короткое замыкание, если || имел более высокий приоритет... разве && не имеет более высокий приоритет? Итак, прежде чем это произойдет || к короткому замыканию, он должен иметь дело с &&?
Нет. Сначала оценивается левая сторона ||. Я добавлю тестовый код в свой ответ
Из этого я знаю, что он сначала оценивает левую сторону, но, судя по всему, что я видел, && имеет более высокий приоритет, что означает, что сначала нужно оценить &&...
Итак, я подумал, что, возможно, компилятор оптимизирует код, так как он видит, что a равно 2 во время компиляции. Я попытался использовать Math.random(), чтобы установить a на что-то, что компилятор не может увидеть во время компиляции, но если это случайное число было 2, ответ был таким же. Мой единственный вывод заключается в том, что он читает || первым, несмотря на их явно более высокий приоритет.
Oy vey, почему это не соблюдает порядок старшинства. Это не может быть глюк. Должна быть какая-то глупая причина...
Я забыл упомянуть, что когда я использовал Math.random(), я использовал ваш исходный код, а не приведенный выше код. Просто чтобы исключить любую другую фанковость. Это странно, хорошо ... я буду продолжать смотреть вокруг
Я попробовал логическое значение 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. Таким образом, в любом порядке кажется, что он идет слева направо.
Давайте продолжить обсуждение в чате.
Но скобки приведут к тому, что я сделаю это... и получу 0?