次の例を見てください。
class Quirky {
public static void main(String[] args) {
int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false
x = 1; // reset
System.out.println((x = y) == x); // true
}
}
Java言語仕様に、右側と比較するために変数の前の値をロードするよう指示する項目(x = y
)があるかどうかは定かではありません。
最初の式がfalse
に評価され、2番目の式がtrue
に評価されるのはなぜですか?私は(x = y)
が最初に評価されることを期待し、それからそれはx
をそれ自身と比較し(3
)そしてtrue
を返すでしょう。
この質問は Java式の中の部分式の評価の順序とは異なります /ここでx
は間違いなく '部分式'ではありません。それは '評価される'よりもむしろ比較のために loaded である必要があります。質問はJava特有であり、面倒な面接のために一般的に作られた実用的ではない構築物とは異なり、x == (x = y)
という表現は実際のプロジェクトから来ました。これはcompare-and-replaceイディオムの1行の置き換えになるはずだった
int oldX = x;
x = y;
return oldX == y;
これは、x86のCMPXCHG命令よりもさらに単純であるため、Javaでは短い表現に値します。
かっこで示された順序で、最初に計算されるべきです。
いいえ。かっこが計算または評価の順序に何らかの(一般的な)影響を与えるというのはよくある誤解です。それらはあなたの表現の部分を特定の木に強制的に強制して、正しいオペランドを仕事のための正しい操作に束縛します。
(そして、あなたがそれらを使わないのなら、この情報は演算子の "優先度"と連想性から来ています。これは言語の構文木がどのように定義されるかの結果です。かっこを使用しますが、単純化して、優先順位の規則には依存していないとします。)
それが終わったら(つまり、あなたのコードがプログラムに解析されたら)それらのオペランドはまだ評価される必要があります、そしてそれがどのように行われるかについての別々の規則があります。 Javaで最初に評価されます。
これはすべての言語に当てはまるわけではないことに注意してください。たとえば、C++では、&&
や||
のような短絡演算子を使用しているのでない限り、オペランドの評価順序は通常指定されていないので、どちらにも依存しないでください。
教師は、「これが先に加算が行われるように」という誤解を招くようなフレーズを使用してオペレータの優先順位を説明するのをやめる必要があります。式x * y + z
が与えられた場合、正しい説明は「演算子の優先順位は、z
とy
の間ではなく、x * y
とz
の間で加算される」となります。
==
はバイナリ 等号演算子 です。
二項演算子の左側のオペランドは完全に評価されているようですbefore右側のオペランドの任意の部分評価されます。
LouisWassermanが言ったように、式は左から右に評価されます。そしてJavaは、「評価」が実際に行うことを気にするのではなく、使用する(不揮発性、最終的な)値を生成することだけを気にします。
//the example values
x = 1;
y = 3;
System.out.println()
の最初の出力を計算するために、以下が行われます。
x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false
そして2番目を計算する:
(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true
x
とy
の初期値に関係なく、2番目の値は常にtrueに評価されます。これは、値の割り当てとそれが割り当てられている変数との比較を効果的に比較しているためですa = b
とb
はその順序で評価されます。定義上は常に同じです。
Java Language Specificationに変数の以前の値のロードを指示する項目があるかどうかはわかりません...
がある。次に仕様の内容が不明な場合は、仕様を読んでthen不明な場合は質問してください。
...右側の
(x = y)
は、括弧で示されている順序で、最初に計算する必要があります。
その文は偽です。 括弧は評価の順序を意味しません。 Javaでは、括弧に関係なく、評価の順序は左から右です。括弧は、評価の順序ではなく、部分式の境界がどこにあるかを決定します。
最初の式が偽と評価されるのに、2番目の式が真と評価されるのはなぜですか?
==
演算子のルールは次のとおりです。左側を評価して値を生成し、右側を評価して値を生成し、値を比較します。比較は式の値です。
つまり、expr1 == expr2
の意味は、temp1 = expr1; temp2 = expr2;
を記述してからtemp1 == temp2
を評価した場合と常に同じです。
左側にローカル変数を持つ=
演算子のルールは、左側を評価して変数を生成し、右側を評価して値を生成し、割り当てを実行し、結果は割り当てられた値です。
まとめます:
x == (x = y)
比較演算子があります。左側を評価して値を生成します-x
の現在の値を取得します。右側を評価します。それは代入ですので、左側を評価して変数を生成します-変数x
-右側を評価します-y
の現在の値-割り当てますx
、および結果は割り当てられた値です。次に、x
の元の値と割り当てられた値を比較します。
(x = y) == x
を演習として行うことができます。繰り返しますが、左側を評価するためのすべてのルールは、右側を評価するすべてのルールの前に発生します。
私は最初に(x = y)が評価されると予想し、それからxをそれ自体と比較し(3)、trueを返します。
あなたの期待は、Javaのルールに関する一連の誤った信念に基づいています。うまくいけば、あなたは今正しい信念を持ち、将来、本当のことを期待するでしょう。
この質問は、「Java式内の部分式の評価順序」とは異なります
この文は偽です。その質問は完全に密接です。
ここでxは間違いなく「部分式」ではありません。
この文も偽です。これは、各例の副次式twiceです。
「評価」するのではなく、比較のためにロードする必要があります。
これが何を意味するのか分かりません。
どうやらまだ多くの誤った信念を持っているようです。私のアドバイスは、誤った信念が真の信念に置き換わるまで、仕様を読むことです。
質問はJava固有のものであり、式x ==(x = y)は、トリッキーなインタビューの質問のためによく作られた、とてつもなく実用的でない構成要素とは異なり、実際のプロジェクトから来ました。
表現の出所は質問に関係ありません。このような式の規則は、仕様で明確に説明されています。それを読んで!
これは、比較と置換のイディオムを1行で置き換えることになっています。
その1行の置換は、コードの読者であるあなたに多大な混乱を引き起こしたので、私はそれが悪い選択であることを提案します。コードをより簡潔に、しかし理解しにくくすることは、勝利ではありません。コードを高速化することはほとんどありません。
ちなみに、C#にはライブラリメソッドとしてcompare and replaceがあり、これはcan機械の指示に注意してください。 Java型システムでは表現できないため、Javaにはそのようなメソッドはありません。
これは、演算子の優先順位と、演算子の評価方法に関連しています。
括弧()は優先順位が高く、左から右に結合性があります。等号 '=='がこの質問の次に来て、左から右への結合性を持ちます。代入 '='は最後に来て、右から左への結合性を持ちます。
式を評価するためのシステム使用スタック式は左から右に評価されます。
今、元の質問に来ます:
int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false
最初のx(1)はスタックにプッシュされます。次にinner(x = y)が評価され、値x(3)のスタックにプッシュされます。これでx(1)はx(3)と比較されるので、結果は偽になります。
x = 1; // reset
System.out.println((x = y) == x); // true
ここで、(x = y)が評価され、xの値が3になり、x(3)がスタックにプッシュされます。等価後に値が変更されたx(3)はスタックにプッシュされます。これでexpressionが評価され、両方が同じになるので結果はtrueです。
同じではありません。左側は常に右側よりも先に評価されます。括弧は実行順序を指定するのではなく、コマンドのグループ化を指定します。
と:
x == (x = y)
あなたは基本的に同じことをしている:
x == y
そして x は比較の後に y の値を持ちます。
ありながら:
(x = y) == x
あなたは基本的に同じことをしている:
x == x
x の後に y の値を取った。そしてそれは常に true を返します。
あなたがチェックしている最初のテストでは1 == 3を行います。
2番目のテストでは、あなたのチェックは3 == 3を行います。
(x = y)は値を割り当て、その値はテストされます。前者の例では、最初にx = 1、それからxに3が割り当てられます。
後者では、xに3が割り当てられています。明らかに3のままです。
この他の、おそらくもっと簡単な例を考えてみましょう:
int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true
ここでは、++x
の事前インクリメント演算子を適用する必要があります比較を行う前に-例の(x = y)
と同様に計算する必要があります比較の前。
ただし、式の評価はまだ左→→→右であるため、最初の比較は実際に1 == 2
であり、2番目の比較は2 == 2
です。
あなたの例でも同じことが起こります。
式は左から右へ評価されます。この場合:
int x = 1;
int y = 3;
x == (x = y)) // false
x == t
- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
x == t
1 == 3 //false
(x = y) == x); // true
t == x
- left (x = y) => x = 3
t = 3
- (x = y) == x
- t == x
- 3 == 3 //true
基本的に最初のステートメントxは1の値を持っていたので、Javaは1 ==を同じではない新しいx変数と比較します。
2番目のものではx = yと言いましたが、これはxの値が変化したことを意味するので、もう一度呼び出すと同じ値になるので、なぜx == xなのか
==は比較等価演算子で、左から右に作用します。
x == (x = y);
ここでは、xの古い代入値がxの新しい代入値と比較されます。(1 == 3)// false
(x = y) == x;
これに対して、ここでは、xの新しい代入値が、比較の直前に代入されたxの新しい保持値と比較されます。
今これを考えなさい
System.out.println((8 + (5 * 6)) * 9);
System.out.println(8 + (5 * 6) * 9);
System.out.println((8 + 5) * 6 * 9);
System.out.println((8 + (5) * 6) * 9);
System.out.println(8 + 5 * 6 * 9);
出力:
342
278
702
342
278
したがって、括弧は算術式ではその主要な役割を果たしているだけで、比較式ではそうではありません。
ここで重要なのは、=
代入演算子の前にある2つの演算子==
と==
のうち、算術演算子/関係演算子の優先順位が=
(Relational Operatorsが優位)であることです。優先順位にもかかわらず、評価の順序はLTR(LEFT TO RIGHT)です。評価順序の後に優先順位が表示されます。そのため、制約の評価に関係なく、LTRです。