Warum ist x == (x = y) nicht dasselbe wie (x = y) == x?
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 mir nicht sicher, ob es ein Element in der Java Language Specification, die vorschreibt laden des vorherigen Wert einer Variablen für den Vergleich mit der rechten Seite (x = y
) die durch die Bestellung konkludent durch Klammern, sollte zuerst berechnet werden.
Warum wird der erste Ausdruck ausgewertet false
, aber die zweite Auswertung true
? Ich hätte erwartet (x = y)
zuerst ausgewertet werden, und dann wäre es zu vergleichen x
mit sich selbst (3
) und zurück true
.
Diese Frage unterscheidet sich von Reihenfolge der Auswertung der Teilausdrücke in einem Java-Ausdruck in diesem x
ist definitiv kein 'Teilausdruck' hier. Es muss geladen für den Vergleich eher als 'bewertet'. Die Frage ist, Java-spezifischen und der Ausdruck x == (x = y)
im Gegensatz zu weit hergeholt unpraktisch Konstrukte allgemein gestaltete für knifflige Fragen im interview, kam aus einem realen Projekt. Es war eigentlich eine one-line-Ersatz für die vergleichen-und-ersetzen-idiom
int oldX = x;
x = y;
return oldX == y;
werden, die, wenn auch einfacher als die x86-Instruktion CMPXCHG, verdient einen kürzeren Ausdruck in Java.
- Der linken Seite wird immer ausgewertet, bevor der rechten Seite. Die Klammern nicht einen Unterschied machen, dass.
- Der Begriff bewerten ist, meiner Meinung nach, nicht anwendbar, weil hier
x
muss nicht bewertet werden, es nur aus dem Speicher geladen. - Die Auswertung des Ausdrucks
x = y
ist sicherlich relevant, und verursacht der Nebeneffekt, dassx
auf den Wert vony
. - Sicher. Ich wusste, dass, und es war die Hauptidee, die mich führen zu
quickReplaceAndCompare()
. - Tun Sie sich und Ihren Teamkollegen einen gefallen und don ' T mix Zustand-mutation in der gleichen Zeile wie Staatsexamen. Dabei drastisch reduziert, die Lesbarkeit des Codes. (Es gibt einige Fälle, in denen es absolut notwendig ist, wegen der Unteilbarkeit Anforderungen, funktioniert aber für diejenigen, die bereits vorhanden sind und deren Ziel es ist, sofort erkannt.)
- Ein Beispiel für die frustration "shortcuts", wie dies verursachen kann, es hat mich den besseren Teil von einer minute, bis diese vollständig genug, um zu sehen, was diese tun. Ich weiß auch C++, und die Regeln sind anders in C++, als Sie hier in Java. In der Tat, in C++ ist dies zu undefiniertem Verhalten, so wirft es alle Arten geistiger roten Fahnen für mich, Bremsen Sie mich nach unten mehr, als Sie wahrscheinlich sollten. Ich wahrscheinlich Lesen Sie über
replaceAndCompare
100 mal schneller als ichquickReplaceAndCompare
. - Die Frage ist, warum Sie wollen, um code zu schreiben wie dieses.
- Schreiben Sie niemals solchen code in der Produktion.
- Ein weiterer Vorrang-Frage: Was sind die Regeln für die Evaluierung, um in Java?
- Off: wenn es C ist, wäre es ein klarer Fall von UB, d.h. der compiler generiert möglicherweise etwas von ihm.
- Jemand hat mir einmal erzählt, dass alle booleschen Ausdrücke haben drei mögliche Werte: True, False und "Bloody Stupid".
- Tut Konstrukte, wie Sie in OP tatsächlich geben eine Garantie von Atomarität?
- Der Schlüssel um deine Frage zu Ihrem falschen glauben, dass Klammern bedeuten Auswertung um. Das ist eine gemeinsame überzeugung, weil, wie wir gelehrt, Mathematik in der Grundschule und weil einige Anfänger-Programmier-Bücher noch get it wrong, aber es ist ein falscher glaube. Dies ist eine ziemlich häufige Frage. Könnten Sie davon profitieren, Lesen Sie meine Artikel über das Thema; Sie sind über C#, aber Sie gelten für Java: ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order ericlippert.com/2009/08/10/precedence-vs-order-redux
- Nein, ich beziehe mich auf Dinge wie dieser. Atomare Operationen sind explizit, also warum sind Sie sofort erkannt. Sorry für die nicht mehr klar.
- Berücksichtigen Sie insbesondere so etwas wie
x() * (y() + z())
Die Reihenfolge von Funktionsaufrufen ist nicht y, dann z, dann ist x, weil "die Klammern kommen zuerst". Die Klammern bestimmen den Teilausdruck Grenzen, nicht die Reihenfolge der Auswertung. Die*
hat zwei Teilausdrücke:x()
und(y() + z())
. Auf der linkenx()
, passiert zuerst. Dann das richtige passiert:y()
genannt wird,z()
genannt wird, und Sie werden summiert. Dann die Multiplikation. Also die+
geschieht, bevor die*
, wie es kommen muss, aber die Operanden an den plus nicht passieren ersten. - Ich denke, dass ein Teil des Problems ist, dass Mathematik-Lehrer siehe Rangfolge der Regeln als der "order of operations", und das Wort "um" Reisen Menschen bis.
- "x ist definitiv kein 'Teilausdruck' hier" - das ist Es wirklich. Warum denken Sie, dass der "laden" den Wert aus der variable wird nicht an Instanz von "evaluation"? Die Auswertung liefert einen Wert-nicht eine variable oder eine memory location.
- Der original-poster ist, wie Sie merken, etwas verwirrt. Jedoch Ihre Aussage, dass die Auswertung liefert einen Wert, und nicht um eine variable, wird nicht unterstützt durch Beweise. Betrachten Sie zum Beispiel
array()[index()] = value();
Die Semantik von Java, die wir alsarray()
, dannindex()
, dannvalue()
; all dies sind Werte. Aber wir müssen dann prüfen, ob die array-Referenz gültig ist und werfen, wenn es nicht ist, und dann prüfen, ob der index gültig ist, und werfen, wenn es nicht ist. Aber absolut der Teilausdruck Links von der Zuweisung nicht den Wert. Es ist eine variable. - Deine Aussage ist auch direkt im Widerspruch zu der von der Java-Spezifikation. Sprachen wie C unterscheiden zwischen "lvalues" und "rvalues"; ein "lvalue" ist der "Wert" erzeugt, durch die Auswertung der linken Seite einer Zuweisung. Dies wird zu Recht kritisiert als verwirrend, Java (und C#) vermeiden Sie dieses problem durch den Verzicht auf die Vorstellung, dass nur belegbare Dinge sind "Werte", und statt dessen nennen Sie das, was Sie sind: Variablen. Die Java-Spezifikation für die Zuordnung macht es sehr klar: "Zuerst der linke operand ausgewertet, um zu produzieren, eine variable..."
- ist definitiv kein Teilausdruck "hier" - auf welcher Grundlage glaubst du das?
- Weil der Ausdruck beinhaltet Betreiber(s).
- Diese Aussage ist falsch; ein Ausdruck muss nicht mit jedem Betreiber. Wieder, Sie würde sehr gut tun zu Lesen der Spezifikation, denn Sie haben sehr viele falsche Vorstellungen. Befreien Sie sich von Ihnen!
- Um zu sehen, von der spec (docs.oracle.com/javase/specs/jls/se11/html/jls-15.html), dass es Ausdrücke ohne Operatoren, können Sie die syntax der
Expression
terminal über mehrere Ebenen, durchPostFixExpression
undExpressionName
. Oder schauen Sie aufMethodInvocation
, derenArgumentList
besteht aus Komma-getrennten Ausdrücken. Wenn jeder Ausdruck erforderlich waren einzubeziehen, die die Operatoren, die Sie nicht bestehen könntex
als argument an eine Methode. - ein Ausdruck, der nicht notwendigerweise Betreiber(s). Hier, Sie werden sehen, dass
Expression
kann einNameExpression
(z.B. bezeichnet eine lokale variable), einePrimaryExpression
(z.B. bezogen auf eine primitive Literale), etc - Blick auf die Umsetzung der
Map.computeIfAbsent()
Schuld und sein Autor auch. Ja, ich weiß, dass Sie berechtigt sind, dies zu tun und ich bin nicht 🙂 - Ich denke, wir reden bei der cross-Zwecke hier, da der Bezeichner "x" erscheint zweimal im Ausdruck, in der Diskussion, einmal auf der linken Seite der Gleichheits-operator, und eine auf der linken Seite eines Zuweisungs-operators. Ich nahm an, dass sich die Diskussion über die ehemalige, seit der OP spricht über "laden" den Wert. Den Teil der Spezifikation, die Sie zitieren, ist über den linken Operanden des zuweisungsoperators. Ich zugeben, dass ich Sprach unpräzis. Mein Punkt ist, dass die "Bewertung" der Links-Ausdruck nicht Ertrag eine Art lazy-Wert, können sich verschieben oder ändern, bevor das Vergleich Betrieb bewertet.
- Meine interpretation des OP ' s mental model war: 1. bewerten
==
Operanden von Links nach rechts. 2.x
braucht keine Bewertung, es ist nur "x" (Fehler). 3. bewerten(x = y)
4. Variablex
wird aktualisiert, um den Wert vony
, der Ausdruck hat den Wert vony
, die3
. 5. bewerten==
Betreiber. 6. Rufen Sie den aktuellen Wert derx
und vergleichen Sie es mit5
. Wenn das der Fall ist, es ist nicht die Reihenfolge der Auswertung, die Sie bekommen haben, verwirrt. Es ist die Bedeutung von "Auswertung x".
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nicht. Es ist ein verbreiteter Irrtum, dass die Klammern haben keine (Allgemeinen) Wirkung auf die Berechnung oder Auswertung um. Sie nur zwingen, die Teile Ihres Ausdrucks in einer bestimmten Struktur, die Bindung des rechten Operanden nach rechts Operationen für den job.
(Und, wenn Sie Sie nicht verwenden, diese information stammt aus dem "Vorrang" und Assoziativität der Operatoren, etwas, das ein Ergebnis davon, wie die Sprache die syntax tree definiert wird. In der Tat, das ist noch genau, wie es funktioniert, wenn Sie Klammern verwenden, aber wir vereinfachen und sagen, dass wir nicht unter Berufung auf etwaige Vorrang-Regeln.)
Sobald das erledigt ist (d.h. sobald Sie Ihren code analysiert wurde, in ein Programm) diese Operanden müssen noch ausgewertet werden, und es gibt unterschiedliche Regeln darüber, wie das geschehen ist: der besagte Regeln (als Andrew hat uns gezeigt) geben an, dass die LHS der operation ausgewertet wird zuerst in Java.
Beachten Sie, dass dies nicht der Fall ist, in allen Sprachen, z.B. in C++, es sei denn, Sie sind mit einem Kurzschluss-Betreiber wie
&&
oder||
die Auswertung der Reihenfolge der Operanden ist in der Regel nicht spezifiziert und man sollte sich auf Sie nicht verlassen oder so.Lehrer müssen damit aufhören zu erklären, Rangfolge mit irreführenden Sätzen wie "das macht der Zusatz geschieht zuerst". Gegeben ein Ausdruck
x * y + z
die richtige Erklärung wäre die "Rangfolge lässt den Zusatz geschehen zwischenx * y
undz
, anstatt zwischeny
undz
", mit keinen Hinweis auf eine "Bestellung".==
ist eine binäre Gleichheits-operator.Als LouisWasserman sagte, der Ausdruck wird von Links nach rechts ausgewertet. Und java ist nicht egal, was "bewerten" tatsächlich, es kümmert sich nur um die Erzeugung einer (non-volatile -, final -) Wert, mit zu arbeiten.
So zu berechnen, die erste Ausgabe von
System.out.println()
wird, wird Folgendes getan:Berechnung und die zweite:
Beachten Sie, dass der zweite Wert wird immer zu true ausgewertet werden, unabhängig von der anfänglichen Werte der
x
undy
, weil Sie effektiv vergleichen die Zuweisung eines Werts an die variable zugewiesen ist, unda = b
undb
wird, bewertet in dieser Reihenfolge, immer per definition identisch.Es ist. Nächstes mal, wenn Sie unklar, was die Spezifikation sagt, bitte Lesen Sie die Spezifikation und dann Fragen, ob es ist, ist noch unklar.
Diese Aussage ist falsch. Klammern bedeuten nicht eine Reihenfolge der Auswertung. In Java die Reihenfolge der Auswertung von Links nach rechts, unabhängig von Klammern. Klammern bestimmen, wo der Teilausdruck Grenzen sind, nicht die Reihenfolge der Auswertung.
Die Regel für die
==
operator: bewerten Sie die Links, um zu produzieren ein Wert, der Auswertung der rechten Seite zu produzieren, ein Wert, Werte vergleichen, der Vergleich ist der Wert des Ausdrucks.In anderen Worten, die Bedeutung von
expr1 == expr2
ist immer das gleiche, als wenn du geschrieben hättesttemp1 = expr1; temp2 = expr2;
und dann ausgewertettemp1 == temp2
.Die Regel für die
=
Bediener eine lokale variable auf der linken Seite ist: bewerten der linken Seite erzeugen einer Variablen, der Auswertung der rechten Seite zu produzieren, einen Wert, der für die Zuweisung durchzuführen, das Ergebnis ist der Wert, der zugewiesen wurde.So setzen Sie zusammen:
Wir haben ein Vergleichsoperator. Auswerten der linken Seite, um zu produzieren ein Wert-bekommen wir den aktuellen Wert von
x
. Auswerten der rechten Seite: das ist eine Zuordnung, so Werten wir die linke Seite zu produzieren, die eine variable -- die variablex
-- wir bewerten die Rechte Seite -- der aktuelle Wert vony
-- zuweisenx
, und das Ergebnis ist der zugewiesene Wert. Vergleichen wir dann den ursprünglichen Wert vonx
auf den Wert, der zugewiesen wurde.Können Sie tun
(x = y) == x
als eine übung. Wieder, denken Sie daran, alle Regeln für die Bewertung der linken Seite passieren, bevor alle Regeln der Auswertung der rechten Seite.Ihre Erwartung basiert auf einer Reihe von falschen Vorstellungen über die Regeln von Java. Hoffentlich haben Sie jetzt die richtigen überzeugungen und werden in der Zukunft erwarten, die wahren Dinge.
Diese Aussage ist falsch. Diese Frage ist völlig angemessen.
Diese Aussage ist auch falsch. Es ist ein Teilausdruck zweimal in jedem Beispiel.
Ich habe keine Ahnung, was das bedeutet.
Anscheinend haben Sie immer noch viele falsche Vorstellungen. Mein Rat ist, dass Sie Lesen Sie die Spezifikation, bis Sie Ihre falschen überzeugungen sind ersetzt durch wahre überzeugungen.
Ist die Herkunft des Ausdrucks ist nicht relevant für die Frage. Die Regeln für diese Ausdrücke sind klar beschrieben in der Spezifikation; Lesen!!!
Da, die one-line-Austausch verursachte eine große Verwirrung in Ihnen, dem Leser des Codes, würde ich vorschlagen, dass es war eine schlechte Wahl. Macht den code kompakter, aber schwerer zu verstehen, ist nicht ein Gewinn. Es ist unwahrscheinlich, um den code schneller.
Übrigens C# hat vergleichen und ersetzen als eine Bibliothek-Methode, die kann werden jitted unten, um eine Maschinen-Instruktion. Ich glaube, dass Java nicht so eine Methode, wie es nicht dargestellt werden kann in dem Java-Typ-system.
Es ist in Bezug auf Operatoren und wie Operatoren werden immer ausgewertet.
Klammern '()' hat einen höheren Rang und hat Assoziativität von Links nach rechts.
Die Gleichheit '==' als Nächstes kommen in dieser Frage und hat Assoziativität von Links nach rechts.
Die Zuweisung '=' kommen letzten und hat Assoziativität von rechts nach Links.
Verwendung im System-stack zu bewerten Ausdruck. Ausdruck wird von Links nach rechts ausgewertet.
Kommt nun zur ursprünglichen Frage:
Ersten x(1) geschoben werden, um Stapel.
dann innere (x = y) wird ausgewertet und schob stack mit x-Wert(3).
Jetzt x(1) verglichen wird, die gegen x(3), so ergibt false.
Hier
(x = y) ausgewertet werden, mittlerweile x-Wert werden 3 und x(3) geschoben werden, um Stapel.
Jetzt x(3), mit dem geänderten Wert nach Gleichheit wird geschoben, um den stack.
Nun-Ausdruck ausgewertet wird, und beide werden gleich sein, damit das Ergebnis true ist.
Es ist nicht das gleiche. Der linken Seite wird immer ausgewertet, bevor der rechten Seite, und die Klammern nicht angeben, um der Hinrichtung, sondern eine Gruppierung von Befehlen.
Mit:
Sind Sie im Grunde das gleiche tun wie:
Und x wird der Wert y nach dem Vergleich.
Zwar mit:
Sind Sie im Grunde das gleiche tun wie:
Nach x nahm y's Wert. Und es wird immer wieder wahr.
In der erste test, der Sie überprüft haben, funktioniert 1 == 3.
In der zweiten Prüfung Ihre Prüfung gilt 3 == 3.
(x = y) weist den Wert und dieser Wert wird getestet. In dem vorherigen Beispiel x = 1, dann x zugewiesen ist 3. Funktioniert 1 == 3?
In der letzteren, x zugeordnet ist 3, und es offensichtlich immer noch 3. Tut 3 == 3?
Betrachten Sie diese andere, vielleicht einfacheres Beispiel:
Hier ist der pre-Inkrement-operator in
++x
angewendet werden müssen vor der Vergleich vorgenommen wird — genau wie(x = y)
in deinem Beispiel berechnet werden müssen vor Vergleich.Jedoch der Auswertung eines Ausdrucks geschieht immer noch Links → zu → Recht, so der erste Vergleich ist eigentlich
1 == 2
während die zweite2 == 2
.Das gleiche passiert in deinem Beispiel.
Ausdrücke werden von Links nach rechts ausgewertet. In diesem Fall:
Grundsätzlich die erste Aussage x hat es den Wert 1
Also Java vergleicht 1 == neue variable x, die nicht die gleiche
In die zweite ein, man sagt x=y, was bedeutet, dass der Wert von x geändert, und so dass, wenn Sie nennen es wieder, es wird der gleiche Wert also, warum es wahr ist, und x ==x
== ist ein Vergleich Gleichheitsoperator und es funktioniert von Links nach rechts.
hier die alte zugeordnete Wert von x ist im Vergleich mit dem neuen Wert zuweisen von x, (1==3)//false
In der Erwägung, dass hier neue weisen Wert von x ist im Vergleich mit der neuen holding-Wert von x zugewiesen, um es kurz vor Vergleich, (3==3)//true
Betrachten Sie nun diese
Somit, Klammern spielt seine wichtige Rolle in arithmetischen Ausdrücken nur nicht im Vergleich Ausdrücken.
x + (x = y)
und(x = y) + x
würde zeigen ein ähnliches Verhalten wie das original mit Vergleichsoperatoren.Die Sache hier ist die arithmatic-Betreiber/relationale Operatoren precedency, um aus den beiden Operatoren
=
vs==
die dominante ist==
(Relationale Operatoren dominiert), wie es vorangeht=
Zuweisungsoperatoren.Trotz der Priorität, die Reihenfolge der Auswertung ist LTR (LINKS NACH RECHTS) Vorrang kommt ins Bild, nach der Auswertung um.
So, Unabhängig von irgendwelchen Einschränkungen Auswertung LTR.
Es ist einfach in den zweiten Vergleich auf der linken Seite ist die Zuordnung nach Zuordnung von y zu x (Links), die Sie dann vergleichen 3 == 3. Im ersten Beispiel, die Sie vergleichen x = 1 mit neuen assign x = 3. Es scheint, dass es immer den aktuellen Zustand des Lesens von Anweisungen von Links nach rechts, von x.
Die Art von Ihnen gestellte Frage ist eine sehr gute Frage, wenn Sie wollen, schreiben Sie ein Java-compiler, oder test-Programme, um zu überprüfen, dass ein Java-compiler korrekt arbeitet. In Java werden diese beiden Ausdrücke müssen die Ergebnisse, die Sie sahen. In C++, zum Beispiel, haben Sie nicht zu - also, wenn jemand wiederverwendet Teile eines C++ - compiler in Java-compiler, könnte man theoretisch feststellen, dass der compiler nicht so verhält, wie es sollte.
Als software-Entwickler, code zu schreiben, der lesbar, verständlich und wartbar sein, beide Versionen des Codes würde als schrecklich. Um zu verstehen, was der code tut, man muss wissen genau, wie die Java-Sprache definiert ist. Wer schreibt sowohl Java als auch C++ code würde schaudern Blick auf den code. Wenn Sie haben zu Fragen, warum eine einzige Zeile code macht, was es macht, dann sollten Sie vermeiden, dass code. (Ich nehme an und hoffe, dass die Jungs, die beantwortet deine "warum" - Frage korrekt, wird sich vermeiden, dass ind der code auch).