web-dev-qa-db-ja.com

Javaの評価順序のルールは何ですか?

私はいくつかのJavaテキストを読んでいて、次のコードを得ました:

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

本文では、著者は明確な説明をしておらず、最後の行の効果は次のとおりです:a[1] = 0;

どうやって評価したのか理解できません。

82
ipkiss

それにもかかわらず、エリック・リッパートの見事な答えは、異なる言語について話しているため、適切に役に立たない。これはJavaです。ここで、Java Language Specificationはセマンティクスの決定的な説明です。特に、 §15.26.1 は、=演算子の評価順序を説明するため、関連しています(右結合であることは誰もが知っていますか?)。この質問で私たちが気にしている部分に少し切り取ります:

左側のオペランド式が配列アクセス式( §15.1 )の場合、多くの手順が必要です。

  • 最初に、左側のオペランド配列アクセス式の配列参照部分式が評価されます。この評価が突然完了すると、同じ理由で割り当て式が突然完了します。 (左側のオペランド配列アクセス式の)インデックス部分式と右側のオペランドは評価されず、割り当ては発生しません。
  • それ以外の場合、左側のオペランド配列アクセス式のインデックス部分式が評価されます。この評価が突然完了すると、同じ理由で割り当て式が突然完了し、右側のオペランドは評価されず、割り当ては発生しません。
  • それ以外の場合、右側のオペランドが評価されます。この評価が突然完了すると、同じ理由で割り当て式が突然完了し、割り当ては発生しません。

[...その後、割り当て自体の実際の意味を説明します。簡潔にするためにここでは無視できます...]

要するに、Javaには非常に厳密に定義された 評価順序 があり、これは任意の演算子またはメソッド呼び出しの引数内でほぼ正確に左から右です。配列の割り当てはより複雑なケースの1つですが、それでもL2Rのままです。 (JLSでは、これらの種類の複雑なセマンティック制約を必要とするコードを記述しないことを推奨しています。そのため、1つの割り当てで十分以上のトラブルに巻き込まれる可能性があります。ステートメントごと!)

CおよびC++は、この領域のJavaとは明らかに異なります。それらの言語定義では、評価の順序が意図的に定義されていないため、より最適化されます。 C#は明らかにJavaに似ていますが、正式な定義を示すことができるほど十分にその文献を知りません。 (ただし、これは言語によって実際に異なります。RubyはTclと同様に厳密にL2Rです。ただし、ここでは関係ない理由で代入演算子自体がありません)およびPythonは L2Rですが、割り当てに関してはR2L です。これは奇妙ですが、そこに行きます。)

33
Donal Fellows
a[b] = b = 0;

1)配列インデックス演算子は、代入演算子よりも優先順位が高い( この回答 を参照):

(a[b]) = b = 0;

2)15.26による。 [〜#〜] jls [〜#〜] の代入演算子

割り当て演算子は12個あります。すべてが構文的に右結合です(右から左にグループ化されます)。したがって、a = b = cはa =(b = c)を意味し、cの値をbに割り当ててから、bの値をaに割り当てます。

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

3)15.7による。 [〜#〜] jls [〜#〜] の評価順序

Javaプログラミング言語は、演算子のオペランドが特定の評価順序、つまり左から右に評価されるように見えることを保証します。

そして

二項演算子の左側のオペランドは、右側のオペランドの一部が評価される前に完全に評価されるように見えます。

そう:

a)(a[b])は最初にa[1]に評価されます

b)その後、(b=0)0に評価されます

c)(a[1] = 0)最後に評価された

5
bup

あなたのコードは次と同等です:

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

結果を説明します。

1

以下のさらに詳細な例を検討してください。

経験則として:

これらの質問を解決する際には、優先順位の規則と関連性の表を読むことができるのが最善です。 http://introcs.cs.princeton.edu/Java/11precedence/

これが良い例です:

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

質問:上記の行の出力は何ですか?

回答:優先順位と結合規則を適用します

ステップ1:優先順位の規則に従って、/および*演算子は+-演算子よりも優先されます。したがって、この方程式を実行する開始点は次のように絞り込まれます。

100/10*2

ステップ2:ルールと優先順位に従って:/と*は同じ優先順位です。

/および*演算子の優先順位は等しいため、これらの演算子間の結合性を調べる必要があります。

これら2つの特定の演算子のアソシエイティビティルールに従って、左から右への方程式の実行を開始します。つまり、100/10が最初に実行されます。

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

ステップ3:方程式の実行状態は次のとおりです。

=3+20-13

ルールと優先順位に従って:+と-は同じ優先順位です。

ここで、演算子+と-演算子の間の結合性を調べる必要があります。これら2つの特定の演算子の結合性に従って、左から右への方程式の実行を開始します。つまり、3 + 20が最初に実行されます。

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

10はコンパイル時の正しい出力です

繰り返しますが、これらの質問を解決するときは、優先順位の規則と関連性の表を用意することが重要です。 http://introcs.cs.princeton.edu/Java/11precedence/

0
Mark Burleigh