Betrachten Sie das folgende Beispiel:
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
}
}
Ich bin nicht sicher, ob es in der Java-Sprachspezifikation ein Element gibt, das das Laden des vorherigen Werts einer Variablen zum Vergleich mit der rechten Seite ( x = y
) vorschreibt, das in der durch Klammern angegebenen Reihenfolge zuerst berechnet werden sollte.
Warum wird der erste Ausdruck als false
, der zweite als bewertet true
? Ich hätte erwartet (x = y)
, zuerst ausgewertet zu werden, und dann würde es x
mit sich selbst verglichen ( 3
) und zurückkehren true
.
Diese Frage unterscheidet sich von der Reihenfolge der Auswertung von Unterausdrücken in einem Java-Ausdruck darin, dass x
es sich hier definitiv nicht um einen Unterausdruck handelt. Es muss für den Vergleich geladen werden, anstatt "ausgewertet" zu werden. Die Frage ist Java-spezifisch und der Ausdruck stammt x == (x = y)
im Gegensatz zu weit hergeholten unpraktischen Konstrukten, die üblicherweise für knifflige Interviewfragen entwickelt wurden, aus einem echten Projekt. Es sollte ein einzeiliger Ersatz für die Vergleichs- und Ersetzungssprache sein
int oldX = x;
x = y;
return oldX == y;
Dies war noch einfacher als die x86-CMPXCHG-Anweisung und verdiente einen kürzeren Ausdruck in Java.
quelle
x = y
ist sicherlich relevant und verursacht den Nebeneffekt,x
der auf den Wert von gesetzt wirdy
.Antworten:
Nein. Es ist ein weit verbreitetes Missverständnis, dass Klammern (allgemeine) Auswirkungen auf die Berechnungs- oder Bewertungsreihenfolge haben. Sie zwingen nur die Teile Ihres Ausdrucks zu einem bestimmten Baum und binden die richtigen Operanden an die richtigen Operationen für den Job.
(Und wenn Sie sie nicht verwenden, stammen diese Informationen aus der "Priorität" und Assoziativität der Operatoren. Dies ist ein Ergebnis der Definition des Syntaxbaums der Sprache. Tatsächlich funktioniert dies immer noch genau so, wenn Sie es tun Verwenden Sie Klammern, aber wir vereinfachen und sagen, dass wir uns dann nicht auf Vorrangregeln verlassen.)
Sobald dies erledigt ist (dh wenn Ihr Code in ein Programm analysiert wurde), müssen diese Operanden noch ausgewertet werden, und es gibt separate Regeln, wie dies gemacht wird: Diese Regeln (wie Andrew uns gezeigt hat) geben an, dass die LHS jeder Operation wird zuerst in Java ausgewertet.
Beachten Sie, dass dies nicht in allen Sprachen der Fall ist. In C ++ ist die Auswertungsreihenfolge von Operanden im Allgemeinen nicht angegeben , es sei denn, Sie verwenden einen Kurzschlussoperator wie
&&
oder||
, und Sie sollten sich in keiner Weise darauf verlassen.Die Lehrer müssen aufhören, die Priorität von Operatoren mit irreführenden Sätzen wie "Dies macht die Hinzufügung zuerst" zu erklären. Bei einem gegebenen Ausdruck wäre
x * y + z
die richtige Erklärung "Vorrang des Operators macht die Addition zwischenx * y
undz
statt zwischeny
undz
", ohne dass eine "Reihenfolge" erwähnt wird.quelle
==
ist ein binärer Gleichheitsoperator .quelle
Wie LouisWasserman sagte, wird der Ausdruck von links nach rechts ausgewertet. Und Java ist es egal, was "evaluieren" tatsächlich tut, es ist nur wichtig, einen (nichtflüchtigen, endgültigen) Wert zu generieren, mit dem gearbeitet werden kann.
Um die erste Ausgabe von zu berechnen
System.out.println()
, wird Folgendes ausgeführt:und um die zweite zu berechnen:
Beachten Sie, dass der zweite Wert unabhängig von den Anfangswerten von
x
und immer als wahr ausgewertet wirdy
, da Sie die Zuordnung eines Werts zu der Variablen, der er zugewiesen ist, effektiv vergleichena = b
undb
in dieser Reihenfolge immer gleich sind per Definition.quelle
Es gibt. Wenn Sie das nächste Mal unklar sind, was in der Spezifikation steht, lesen Sie bitte die Spezifikation und stellen Sie dann die Frage, ob sie unklar ist.
Diese Aussage ist falsch. Klammern implizieren keine Reihenfolge der Bewertung . In Java ist die Reihenfolge der Auswertung unabhängig von Klammern von links nach rechts. Klammern bestimmen, wo sich die Unterausdrucksgrenzen befinden, nicht die Reihenfolge der Auswertung.
Die Regel für den
==
Operator lautet: Bewerten Sie die linke Seite, um einen Wert zu erzeugen, bewerten Sie die rechte Seite, um einen Wert zu erzeugen, vergleichen Sie die Werte, der Vergleich ist der Wert des Ausdrucks.Mit anderen Worten, die Bedeutung von
expr1 == expr2
ist immer dieselbe, als ob Sie geschriebentemp1 = expr1; temp2 = expr2;
und dann ausgewertet hättentemp1 == temp2
.Die Regel für den
=
Operator mit einer lokalen Variablen auf der linken Seite lautet: Auswerten der linken Seite, um eine Variable zu erzeugen, Auswerten der rechten Seite, um einen Wert zu erzeugen, Ausführen der Zuweisung, Ergebnis ist der zugewiesene Wert.Also setze es zusammen:
Wir haben einen Vergleichsoperator. Bewerten Sie die linke Seite, um einen Wert zu erhalten - wir erhalten den aktuellen Wert von
x
. Bewerten Sie die rechte Seite: Dies ist eine Zuweisung, also bewerten wir die linke Seite, um eine Variable zu erzeugen - die Variablex
- wir bewerten die rechte Seite - den aktuellen Wert vony
- weisen Sie sie zux
, und das Ergebnis ist der zugewiesene Wert. Wir vergleichen dann den ursprünglichen Wert vonx
mit dem zugewiesenen Wert.Sie können
(x = y) == x
als Übung tun . Denken Sie auch hier daran, dass alle Regeln für die Bewertung der linken Seite vor allen Regeln für die Bewertung der rechten Seite gelten .Ihre Erwartung basiert auf einer Reihe falscher Überzeugungen über die Regeln von Java. Hoffentlich haben Sie jetzt die richtigen Überzeugungen und werden in Zukunft wahre Dinge erwarten.
Diese Aussage ist falsch. Diese Frage ist völlig deutsch.
Diese Aussage ist auch falsch. Es ist in jedem Beispiel zweimal ein Unterausdruck .
Ich habe keine Ahnung, was das bedeutet.
Anscheinend haben Sie immer noch viele falsche Überzeugungen. Mein Rat ist, dass Sie die Spezifikation lesen, bis Ihre falschen Überzeugungen durch wahre Überzeugungen ersetzt werden.
Die Herkunft des Ausdrucks ist für die Frage nicht relevant. Die Regeln für solche Ausdrücke sind in der Spezifikation klar beschrieben; Lies es!
Da dieser einzeilige Ersatz bei Ihnen, dem Leser des Codes, große Verwirrung stiftete, würde ich vorschlagen, dass dies eine schlechte Wahl war. Es ist kein Gewinn, den Code präziser, aber schwerer zu verstehen. Es ist unwahrscheinlich, dass der Code schneller wird.
Im Übrigen verfügt C # über eine Vergleichs- und Ersetzungsmethode als Bibliotheksmethode, die auf eine Maschinenanweisung reduziert werden kann . Ich glaube, Java hat keine solche Methode, da sie im Java-Typsystem nicht dargestellt werden kann.
quelle
Dies hängt mit der Priorität der Operatoren und der Bewertung der Operatoren zusammen.
Klammern '()' haben eine höhere Priorität und Assoziativität von links nach rechts. Gleichheit '==' kommt in dieser Frage als nächstes und hat Assoziativität von links nach rechts. Zuweisung '=' kommt zuletzt und hat Assoziativität von rechts nach links.
System verwendet Stack, um den Ausdruck auszuwerten. Der Ausdruck wird von links nach rechts ausgewertet.
Nun kommt die ursprüngliche Frage:
Zuerst wird x (1) zum Stapeln gedrückt. dann wird inner (x = y) ausgewertet und zum Stapeln mit dem Wert x (3) verschoben. Jetzt wird x (1) mit x (3) verglichen, sodass das Ergebnis falsch ist.
Hier wird (x = y) ausgewertet, jetzt wird der x-Wert 3 und x (3) wird zum Stapeln verschoben. Jetzt wird x (3) mit geändertem Wert nach Gleichheit zum Stapeln verschoben. Jetzt wird der Ausdruck ausgewertet und beide sind gleich, sodass das Ergebnis wahr ist.
quelle
Es ist nicht das Gleiche. Die linke Seite wird immer vor der rechten Seite ausgewertet, und die Klammern geben keine Ausführungsreihenfolge an, sondern eine Gruppierung von Befehlen.
Mit:
Sie machen im Grunde das Gleiche wie:
Und x hat nach dem Vergleich den Wert y .
Während mit:
Sie machen im Grunde das Gleiche wie:
Nachdem x den Wert von y angenommen hat. Und es wird immer wahr zurückkehren .
quelle
Im ersten Test, den Sie überprüfen, wird 1 == 3 ausgeführt.
Im zweiten Test führt Ihre Überprüfung 3 == 3 aus.
(x = y) weist den Wert zu und dieser Wert wird getestet. Im ersteren Beispiel wird zuerst x = 1, dann wird x 3 zugewiesen. Ist 1 == 3?
In letzterem wird x 3 zugewiesen, und offensichtlich ist es immer noch 3. Ist 3 == 3?
quelle
Betrachten Sie dieses andere, vielleicht einfachere Beispiel:
Hier muss der Vorinkrement-Operator in
++x
angewendet werden, bevor der Vergleich durchgeführt wird - genau wie(x = y)
in Ihrem Beispiel muss er vor dem Vergleich berechnet werden .Die Auswertung der Ausdrücke erfolgt jedoch weiterhin von links → nach → rechts , sodass der erste Vergleich tatsächlich
1 == 2
während des zweiten erfolgt2 == 2
.Das gleiche passiert in Ihrem Beispiel.
quelle
Ausdrücke werden von links nach rechts ausgewertet. In diesem Fall:
quelle
Grundsätzlich hatte die erste Anweisung x den Wert 1. Java vergleicht also 1 == mit einer neuen x-Variablen, die nicht dieselbe sein wird
Im zweiten haben Sie x = y gesagt, was bedeutet, dass sich der Wert von x geändert hat. Wenn Sie ihn erneut aufrufen, ist er der gleiche Wert, weshalb er wahr ist und x == x
quelle
== ist ein Vergleichsgleichheitsoperator und funktioniert von links nach rechts.
hier wird der alte zugewiesene Wert von x mit dem neuen zugewiesenen Wert von x verglichen, (1 == 3) // false
Während hier der neue Zuweisungswert von x mit dem neuen Haltewert von x verglichen wird, der ihm unmittelbar vor dem Vergleich zugewiesen wurde, (3 == 3) // true
Betrachten Sie dies nun
Daher spielen Klammern ihre Hauptrolle in arithmetischen Ausdrücken, nur nicht in Vergleichsausdrücken.
quelle
x + (x = y)
und(x = y) + x
würde ein ähnliches Verhalten wie das Original mit Vergleichsoperatoren zeigen.Die Sache hier ist die Rangfolge der arithmatischen Operatoren / relationalen Operatoren aus den beiden Operatoren
=
gegenüber==
der dominanten==
(relationale Operatoren dominieren), da sie den=
Zuweisungsoperatoren vorausgeht . Trotz der Rangfolge ist die Reihenfolge der Bewertung LTR (LINKS NACH RECHTS). Die Priorität wird nach der Bewertungsreihenfolge angezeigt. Unabhängig von Einschränkungen ist die Bewertung also LTR.quelle
Im zweiten Vergleich auf der linken Seite ist es einfach, die Zuordnung nach der Zuweisung von y zu x (links) vorzunehmen und dann 3 == 3 zu vergleichen. Im ersten Beispiel vergleichen Sie x = 1 mit der neuen Zuweisung von x = 3. Es scheint, dass Es werden immer Anweisungen zum Lesen des aktuellen Status von links nach rechts von x verwendet.
quelle
Die Art der Frage, die Sie gestellt haben, ist eine sehr gute Frage, wenn Sie einen Java-Compiler schreiben oder Programme testen möchten, um zu überprüfen, ob ein Java-Compiler ordnungsgemäß funktioniert. In Java müssen diese beiden Ausdrücke die Ergebnisse liefern, die Sie gesehen haben. In C ++ zum Beispiel müssen sie das nicht. Wenn also jemand Teile eines C ++ - Compilers in seinem Java-Compiler wiederverwendet, stellen Sie möglicherweise theoretisch fest, dass sich der Compiler nicht so verhält, wie er sollte.
Als Softwareentwickler, der Code schreibt, der lesbar, verständlich und wartbar ist, werden beide Versionen Ihres Codes als schrecklich angesehen. Um zu verstehen, was der Code tut, muss man genau wissen , wie die Java-Sprache definiert ist. Jemand, der sowohl Java- als auch C ++ - Code schreibt, würde beim Betrachten des Codes schaudern. Wenn Sie sich fragen müssen, warum eine einzelne Codezeile das tut, was sie tut, sollten Sie diesen Code vermeiden. (Ich nehme an und hoffe, dass die Leute, die Ihre "Warum" -Frage richtig beantwortet haben, selbst diese Ind des Codes ebenfalls vermeiden werden).
quelle