Issue
I was testing the precedence between &&
and ||
and I had an example that was confusing. In Java, &&
has higher operator precedence than the operator ||
.
So if we have those 3 expressions:
//expr1 = true , expr2 = false; expr3 = false;
if(expr1 || expr2 && expr3);
It should be evaluated as:
if(expr1 || (expr2 && expr3));
So expr2 && expr3
should be evaluated before expr1
. However, this example:
int a1 = 10;
int a2 = 20;
System.out.println(a1 < a2 || ++a1 > a2 && ++a2 < a1);
System.out.println(a1);
System.out.println(a2);
Outputs:
true
10
20
That proves that only a1 < a2
is evaluated.
Can you explain why this is the case?
Solution
The expression is short-circuiting. From the link:
when the first argument of the AND function evaluates to false, the overall value must be false; and when the first argument of the OR function evaluates to true, the overall value must be true.
It sees that the rest of the condition doesn't matter because one of the operands of ||
is already true (10 < 20). If one of the operands is true, then no matter what the rest of the condition is, it's true.
You may use bitwise &
and |
to prevent this.
But, the ( expr2 && expr3 ) should be evaluated before expr1, no ?
No. You have to separate concepts of precedence and evaluation order.
Precedence: Dictates the parenthesization of an expression, not the order in which an expression is evaluated. For an example:
true || false && false
Is parenthesized to this because
&&
has higher precedence than||
:true || (false && false)
This does not mean that things in parentheses is evaluated first in Java's case. Precedence just clarifies what the operands of an operator are, in this case
false
andfalse
, where as in this case:(true || false) && (false || false)
The operands for
&&
aretrue
andfalse
, notfalse
andfalse
.Evaluation Order: Describes in what order each operand is evaluated and operator is applied and is sometimes language specific. This dictates how an expression is evaluated, unlike precedence.
In this case, your example:
true || false && false
As established earlier, becomes this due to precedence:
true || (false && false)
But Java, unlike C++, JavaScript, or a number of other languages has a strictly left to right evaluation. Per the Java Language Specification:
15.7. Evaluation Order
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
15.7.1. Evaluate Left-Hand Operand First
The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.
So, when you have:
true || (false && false)
Java first evaluates the left operand which turns out to be true
. Then the whole condition short circuits. The right operand of ||
in the parentheses is never evaluated at all. The same goes for your other example:
a1 < a2 || (++a1 > a2 && ++a2 < a1)
^^^^^^^^^^^^^^^^^^^^^^^^
Step 0, precedence and parenthesization
a1 < a2 || (++a1 > a2 && ++a2 < a1)
^^^^^^^
Step 1, left operand evaluated, variables resolved to values 10 and 20, condition is true
true || (++a1 > a2 && ++a2 < a1)
^^^^
Step 2, short circuits, left operand is not evaluated
Take another more complex example:
false || false || true && (false || true) || false
Due to precedence, it becomes:
false || false || (true && (false || true)) || false
Then, evaluation begins, left to right:
false || false || (true && (false || true)) || false
^^^^^^^^^^^^^^
Step 1, false || false, does not short circuit, right operand is evaluated, is false
false || (true && (false || true)) || false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Step 2, false || (true && (false || true)), does not short circuit, right operand is evaluated
Step 2A, (true && (false || true)), does not short circuit, right operand is evaluated
Step 2B, (false || true), does not short circuit, right operand is evaluated, is true
Step 2C, (true && true), does not short circuit, right operand is evaluated, is true
Step 2D, false || true, does not short circuit, right operand is evaluated, is true
true || false
^^^^
Step 3, true || false short circuits, right operand is not evaluated, is true
Thus the whole expression evaluates to true
. The whole expression was evaluated left to right the whole way through. Precedence only dictated the operands of an operator via parenthesization, not the evaluation order.
Further reading at Eric Lippert's explanatory article on precedence vs associativity vs evaluation order as mentioned by Daniel Pryden, it clears up a lot of the confusion.
The main takeaway is that precedence does not dictate in what an expression is evaluated. It only dictates how an expression should be parenthesized. Evaluation order, on the other hand, tells us exactly how an expression is evaluated, and in Java's case is always left to right.
Answered By - Andrew Li
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.