development

Java의 평가 순서에 대한 규칙은 무엇입니까?

big-blog 2020. 9. 23. 07:52
반응형

Java의 평가 순서에 대한 규칙은 무엇입니까?


일부 Java 텍스트를 읽고 다음 코드를 받았습니다.

int[] a = {4,4};
int b = 1;
a[b] = b = 0;

본문에서 저자는 명확한 설명을하지 않았으며 마지막 줄의 효과는 다음과 같습니다. a[1] = 0;

이해가 잘 안됩니다. 평가는 어떻게 이루어 졌습니까?


사람들이 항상 이것을 오해하기 때문에 이것을 매우 명확하게 말하겠습니다.

하위 표현식의 평가 순서는 연관성 및 우선 순위와 무관 합니다. 연관성과 우선 순위는 연산자 가 실행되는 순서를 결정 하지만 하위 표현식 이 평가 되는 순서는 결정 하지 않습니다 . 귀하의 질문은 하위 표현식 이 평가 되는 순서에 관한 것 입니다.

고려하십시오 A() + B() + C() * D(). 곱셈은 ​​덧셈보다 우선 순위가 높고 덧셈은 왼쪽 연관성이므로 이것은 (A() + B()) + (C() * D())첫 번째 덧셈이 두 번째 덧셈 전에 발생하고 곱셈이 두 번째 덧셈 전에 발생한다는 것을 알려주 는 것과 동일합니다 . A (), B (), C () 및 D ()가 어떤 순서로 호출되는지 알려주지 않습니다! (또한 곱셈이 첫 번째 덧셈 전후에 발생하는지 여부를 알려주지 않습니다.) 다음과 같이 컴파일하여 우선 순위 및 연관성 규칙을 완벽하게 준수 할 수 있습니다 .

d = D()          // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b      // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last

모든 우선 순위 및 연관성 규칙이 거기에 따릅니다. 첫 번째 더하기는 두 번째 더하기 전에 발생하고 곱하기는 두 번째 더하기 전에 발생합니다. 분명히 우리는 A (), B (), C () 및 D ()에 대한 호출을 임의의 순서로 수행 할 수 있으며 여전히 우선 순위 및 연관성의 규칙을 준수합니다!

하위 표현식이 평가되는 순서를 설명하려면 우선 순위 및 연관성 규칙 과 관련이없는 규칙 필요합니다 . Java (및 C #)의 관련 규칙은 "하위 표현식은 왼쪽에서 오른쪽으로 평가됩니다"입니다. A ()가 C ()의 왼쪽에 나타나기 때문에 C ()가 곱셈에 관련되고 A ()가 덧셈에만 관련된다는 사실에 관계없이 A ()가 먼저 평가 됩니다.

이제 질문에 답할 수있는 충분한 정보가 있습니다. 에서 a[b] = b = 0연관성 말의 규칙이 있음을 a[b] = (b = 0);하지만 그 뜻은 아닙니다 b=0첫번째 실행을! 우선 순위 규칙은 인덱싱이 할당보다 우선 순위가 높다고 말하지만 인덱서가 가장 오른쪽 할당보다 먼저 실행된다는 것을 의미하지는 않습니다 .

(업데이트 :이 답변의 이전 버전에는 내가 수정 한 섹션에 작고 실질적으로 중요하지 않은 누락이 있습니다.이 규칙이 Java 및 C #에서 왜 합리적인지 설명하는 블로그 기사를 작성했습니다 .https : // ericlippert.com/2019/01/18/indexer-error-cases/ )

것을 우선 순위와는 단지 우리에게 영의 할당b일어나야 하기 전에 에 할당 a[b], 0이기 때문에 계산하는 색인 작업에 할당 된 값의 할당. 우선 순위와 여부에 대한 연관성 혼자 말 아무것도 a[b]평가 또는 후에b=0 .

다시 말하지만 이것은 다음과 동일합니다 A()[B()] = C().-우리가 아는 것은 할당 전에 인덱싱이 이루어져야한다는 것입니다. A (), B () 또는 C ()가 우선 순위와 연관성을 기반으로 먼저 실행되는지 여부는 알 수 없습니다 . 우리는 그것을 말해주는 또 다른 규칙이 필요합니다.

규칙은 "먼저 무엇을할지 선택할 수있는 경우 항상 왼쪽에서 오른쪽으로 이동"입니다. 그러나이 특정 시나리오에는 흥미로운 주름이 있습니다. null 컬렉션 또는 범위를 벗어난 인덱스로 인해 발생한 예외의 부작용이 할당의 왼쪽 계산의 일부로 간주됩니까, 아니면 할당 자체 계산의 일부로 간주됩니까? 자바는 후자를 선택합니다. (물론 이것은 올바른 코드가 null을 역 참조하거나 처음부터 잘못된 인덱스를 전달하지 않기 때문에 코드가 이미 잘못된 경우 에만 중요한 차이입니다 .)

그래서 어떻게 되나요?

  • a[b]의 왼쪽에 b=0소위, a[b]실행은 첫째 , 결과 a[1]. 그러나이 인덱싱 작업 유효성 확인 이 지연됩니다.
  • 그런 다음 b=0발생합니다.
  • 그런 다음 a유효하고 a[1]범위 내에 있는 확인이 발생합니다.
  • 값 할당은 a[1]마지막 발생합니다.

따라서이 특정 경우에는 처음에 올바른 코드에서 발생하지 않아야하는 드문 오류 사례에 대해 고려해야 할 몇 가지 미묘한 점이 있지만 일반적으로 추론 할 수 있습니다. 왼쪽에있는 일이 오른쪽에있는 일보다 먼저 발생합니다 . 그것이 당신이 찾고있는 규칙입니다. 우선 순위와 연관성에 대한 이야기는 혼란스럽고 관련성이 없습니다.

사람들은이 문제를 항상 잘못 이해 합니다 . 더 잘 알아야하는 사람들도 마찬가지입니다. 규칙을 잘못 언급 한 프로그래밍 책을 너무 많이 편집 했으므로 많은 사람들이 우선 순위 / 연관성 및 평가 순서 사이의 관계에 대해 완전히 잘못된 믿음을 가지고 있다는 사실, 즉 실제로 그러한 관계가 없다는 것은 놀라운 일이 아닙니다. ; 그들은 독립적입니다.

이 주제에 관심이 있으시면 해당 주제에 대한 내 기사를 참조하십시오.

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

C #에 관한 것이지만 대부분의 내용은 Java에도 동일하게 적용됩니다.


그럼에도 불구하고 Eric Lippert의 뛰어난 답변은 다른 언어에 대해 이야기하고 있기 때문에 적절하게 도움이되지 않습니다. 이것은 Java입니다. 여기서 Java 언어 사양은 의미에 대한 명확한 설명입니다. 특히 §15.26.1=연산자에 대한 평가 순서를 설명하기 때문에 관련이 있습니다 (우리 모두는 그것이 오른쪽 연관성이라는 것을 알고 있습니까?). 이 질문에서 우리가 관심을 갖는 비트로 조금 줄이십시오.

왼쪽 피연산자식이 배열 액세스 식 ( §15.13 )이면 여러 단계가 필요합니다.

  • First, the array reference subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no assignment occurs.
  • Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no assignment occurs.
  • Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.

[… it then goes on to describe the actual meaning of the assignment itself, which we can ignore here for brevity …]

In short, Java has a very closely defined evaluation order that is pretty much exactly left-to-right within the arguments to any operator or method call. Array assignments are one of the more complex cases, but even there it's still L2R. (The JLS does recommend that you don't write code that needs these sorts of complex semantic constraints, and so do I: you can get into more than enough trouble with just one assignment per statement!)

C and C++ are definitely different to Java in this area: their language definitions leave evaluation order undefined deliberately to enable more optimizations. C# is like Java apparently, but I don't know its literature well enough to be able to point to the formal definition. (This really varies by language though, Ruby is strictly L2R, as is Tcl — though that lacks an assignment operator per se for reasons not relevant here — and Python is L2R but R2L in respect of assignment, which I find odd but there you go.)


a[b] = b = 0;

1) array indexing operator has higher precedence then assignment operator (see this answer):

(a[b]) = b = 0;

2) According to 15.26. Assignment Operators of JLS

There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.

(a[b]) = (b=0);

3) According to 15.7. Evaluation Order of JLS

The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

and

The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

So:

a) (a[b]) evaluated first to a[1]

b) then (b=0) evaluated to 0

c) (a[1] = 0) evaluated last


Your code is equivalent to:

int[] a = {4,4};
int b = 1;
c = b;
b = 0;
a[c] = b;

which explains the result.


Consider another more in-depth example below.

As a General Rule of Thumb:

It's best to have a table of the Order of Precedence Rules and Associativity available to read when solving these questions e.g. http://introcs.cs.princeton.edu/java/11precedence/

Here is a good example:

System.out.println(3+100/10*2-13);

Question: What's the Output of the above Line?

Answer: Apply the Rules of Precedence and Associativity

Step 1: According to rules of precedence: / and * operators take priority over + - operators. Therefore the starting point to execute this equation will the narrowed to:

100/10*2

Step 2: According to the rules and precedence: / and * are equal in precedence.

As / and * operators are equal in precedence, we need to look at the associativity between those operators.

According to the ASSOCIATIVITY RULES of these two particular operators, we start executing the equation from the LEFT TO RIGHT i.e. 100/10 gets executed first:

100/10*2
=100/10
=10*2
=20

Step 3: The equation is now in the following state of execution:

=3+20-13

According to the rules and precedence: + and - are equal in precedence.

We now need to look at the associativity between the operators + and - operators. According to the associativity of these two particular operators, we start executing the equation from the LEFT to RIGHT i.e. 3+20 gets executed first:

=3+20
=23
=23-13
=10

10 is the correct output when compiled

Again, it is important to have a table of the Order of Precedence Rules and Associativity with you when solving these questions e.g. http://introcs.cs.princeton.edu/java/11precedence/

참고URL : https://stackoverflow.com/questions/6800590/what-are-the-rules-for-evaluation-order-in-java

반응형