Ich lese Java-Text und habe den folgenden Code erhalten:
int[] a = {4,4};
int b = 1;
a[b] = b = 0;
Im Text gab der Autor keine klare Erklärung und die Wirkung der letzten Zeile ist: a[1] = 0;
Ich bin mir nicht sicher, ob ich verstehe: Wie ist die Bewertung verlaufen?
java
operator-precedence
ipkiss
quelle
quelle
Antworten:
Lassen Sie mich das ganz klar sagen, weil die Leute das die ganze Zeit falsch verstehen:
Die Reihenfolge der Auswertung von Unterausdrücken ist unabhängig von Assoziativität und Vorrang . Assoziativität und Priorität bestimmen, in welcher Reihenfolge die Operatoren ausgeführt werden, bestimmen jedoch nicht , in welcher Reihenfolge die Unterausdrücke ausgewertet werden. Ihre Frage bezieht sich auf die Reihenfolge, in der Unterausdrücke ausgewertet werden.
Überlegen Sie
A() + B() + C() * D()
. Die Multiplikation hat eine höhere Priorität als die Addition, und die Addition ist linksassoziativ. Dies entspricht jedoch dem(A() + B()) + (C() * D())
Wissen, dass die erste Addition vor der zweiten Addition und die Multiplikation vor der zweiten Addition erfolgt. Es sagt Ihnen nicht, in welcher Reihenfolge A (), B (), C () und D () aufgerufen werden! (Es sagt Ihnen auch nicht, ob die Multiplikation vor oder nach der ersten Addition erfolgt.) Es wäre durchaus möglich, die Vorrang- und Assoziativitätsregeln zu befolgen, indem Sie Folgendes kompilieren:Dort werden alle Vorrang- und Assoziativitätsregeln befolgt - die erste Addition erfolgt vor der zweiten Addition und die Multiplikation erfolgt vor der zweiten Addition. Natürlich können wir die Aufrufe von A (), B (), C () und D () in beliebiger Reihenfolge ausführen und trotzdem die Vorrang- und Assoziativitätsregeln einhalten!
Wir brauchen eine Regel, die nicht mit den Vorrang- und Assoziativitätsregeln zusammenhängt, um die Reihenfolge zu erklären, in der die Unterausdrücke ausgewertet werden. Die relevante Regel in Java (und C #) lautet "Unterausdrücke werden von links nach rechts ausgewertet". Da A () links von C () erscheint, wird A () zuerst ausgewertet, unabhängig davon, dass C () an einer Multiplikation beteiligt ist und A () nur an einer Addition beteiligt ist.
Jetzt haben Sie genug Informationen, um Ihre Frage zu beantworten. In
a[b] = b = 0
den Regeln der Assoziativität heißt es, dass diesa[b] = (b = 0);
aber nicht bedeutet, dass dasb=0
zuerst läuft! Die Vorrangregeln besagen, dass die Indizierung eine höhere Vorrangstellung als die Zuweisung hat. Dies bedeutet jedoch nicht, dass der Indexer vor der Zuweisung ganz rechts ausgeführt wird .(UPDATE: Eine frühere Version dieser Antwort hatte einige kleine und praktisch unwichtige Auslassungen im folgenden Abschnitt, die ich korrigiert habe. Ich habe auch einen Blog-Artikel geschrieben, in dem beschrieben wird, warum diese Regeln in Java und C # sinnvoll sind: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
Rangfolge und Assoziativität uns nur sagen , dass die Zuordnung von Null
b
passieren muss , bevor die Zuordnung zua[b]
, weil die Zuordnung von Null berechnet der Wert, der in dem Indexierungsvorgang zugeordnet ist. Vorrang und Assoziativität allein sagen nichts darüber aus, ob das vor oder nach dema[b]
bewertet wird .b=0
Dies ist wiederum genau das Gleiche wie:
A()[B()] = C()
- Wir wissen nur, dass die Indizierung vor der Zuweisung erfolgen muss. Wir wissen nicht, ob A (), B () oder C () zuerst ausgeführt werden, basierend auf Vorrang und Assoziativität . Wir brauchen eine andere Regel, um uns das zu sagen.Die Regel lautet wiederum: "Wenn Sie die Wahl haben, was zuerst zu tun ist, gehen Sie immer von links nach rechts." In diesem speziellen Szenario gibt es jedoch eine interessante Falte. Wird der Nebeneffekt einer ausgelösten Ausnahme, der durch eine Nullsammlung oder einen Index außerhalb des Bereichs verursacht wird, als Teil der Berechnung der linken Seite der Zuweisung oder als Teil der Berechnung der Zuweisung selbst betrachtet? Java wählt letzteres. (Dies ist natürlich eine Unterscheidung, die nur dann von Bedeutung ist, wenn der Code bereits falsch ist , da der richtige Code nicht null dereferenziert oder überhaupt einen schlechten Index übergibt.)
Was passiert also?
a[b]
ist links vonb=0
, alsoa[b]
läuft das zuerst , was dazu führta[1]
. Die Überprüfung der Gültigkeit dieses Indizierungsvorgangs wird jedoch verzögert.b=0
passiert das.a
die gültig ist unda[1]
sich in Reichweite befindeta[1]
erfolgt zuletzt.Obwohl in diesem speziellen Fall einige Feinheiten für die seltenen Fehlerfälle zu berücksichtigen sind, die überhaupt nicht im richtigen Code auftreten sollten, können Sie im Allgemeinen argumentieren: Dinge auf der linken Seite passieren vor Dingen auf der rechten Seite . Das ist die Regel, nach der Sie suchen. Die Rede von Vorrang und Assoziativität ist sowohl verwirrend als auch irrelevant.
Die Leute verstehen dieses Zeug die ganze Zeit falsch , sogar Leute, die es besser wissen sollten. Ich habe viel zu viele Programmierbücher bearbeitet , in denen die Regeln falsch angegeben wurden. Daher ist es nicht verwunderlich, dass viele Menschen völlig falsche Vorstellungen über die Beziehung zwischen Vorrang / Assoziativität und Bewertungsreihenfolge haben - nämlich, dass es in Wirklichkeit keine solche Beziehung gibt ;; Sie sind unabhängig.
Wenn Sie dieses Thema interessiert, lesen Sie meine Artikel zum Thema zur weiteren Lektüre:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Es geht um C #, aber das meiste davon gilt auch für Java.
quelle
Eric Lipperts meisterhafte Antwort ist dennoch nicht richtig hilfreich, da es sich um eine andere Sprache handelt. Dies ist Java, wobei die Java-Sprachspezifikation die endgültige Beschreibung der Semantik ist. Insbesondere ist §15.26.1 relevant, da dies die Bewertungsreihenfolge für den
=
Bediener beschreibt (wir alle wissen, dass es richtig assoziativ ist, ja?). Reduzieren Sie es ein wenig auf die Teile, die uns in dieser Frage wichtig sind:[… Anschließend wird die tatsächliche Bedeutung der Aufgabe selbst beschrieben, die wir hier der Kürze halber ignorieren können…]
Kurz gesagt, Java hat eine sehr genau definierte Auswertungsreihenfolge, die innerhalb der Argumente für jeden Operator oder Methodenaufruf ziemlich genau von links nach rechts ist. Array-Zuweisungen sind einer der komplexeren Fälle, aber selbst dort ist es immer noch L2R. (Das JLS empfiehlt, keinen Code zu schreiben, der diese Art komplexer semantischer Einschränkungen benötigt , und ich auch: Mit nur einer Zuweisung pro Anweisung können Sie mehr als genug Probleme bekommen!)
C und C ++ unterscheiden sich in diesem Bereich definitiv von Java: Ihre Sprachdefinitionen lassen die Bewertungsreihenfolge bewusst undefiniert, um weitere Optimierungen zu ermöglichen. C # ist anscheinend wie Java, aber ich kenne seine Literatur nicht gut genug, um auf die formale Definition verweisen zu können. (Dies variiert jedoch wirklich je nach Sprache. Ruby ist streng L2R, ebenso wie Tcl - obwohl es aus hier nicht relevanten Gründen keinen Zuweisungsoperator an sich gibt - und Python ist L2R, aber R2L in Bezug auf die Zuweisung , was ich seltsam finde, aber los geht's .)
quelle
a[-1]=c
,c
wird ausgewertet, bevor-1
als ungültig erkannt wird.1) Der Array-Indizierungsoperator hat eine höhere Priorität als der Zuweisungsoperator (siehe diese Antwort ):
2) Nach 15.26. Zuweisungsoperatoren von JLS
3) Gemäß 15.7. Bewertungsreihenfolge von JLS
und
So:
a)
(a[b])
zuerst ausgewerteta[1]
b) dann
(b=0)
ausgewertet bis0
c)
(a[1] = 0)
zuletzt ausgewertetquelle
Ihr Code entspricht:
das erklärt das Ergebnis.
quelle
Betrachten Sie unten ein weiteres ausführlicheres Beispiel.
Als allgemeine Faustregel:
Es ist am besten, eine Tabelle mit den Regeln und der Assoziativität der Rangfolge zur Verfügung zu haben, die Sie bei der Lösung dieser Fragen lesen können, z. B. http://introcs.cs.princeton.edu/java/11precedence/.
Hier ist ein gutes Beispiel:
Frage: Was ist die Ausgabe der obigen Zeile?
Antwort: Wenden Sie die Vorrang- und Assoziativitätsregeln an
Schritt 1: Gemäß den Vorrangregeln: / und * haben Vorrang vor + - Operatoren. Daher wird der Ausgangspunkt zum Ausführen dieser Gleichung auf Folgendes eingegrenzt:
Schritt 2: Gemäß den Regeln und Vorrang: / und * haben Vorrang.
Da / und * Operatoren Vorrang haben, müssen wir die Assoziativität zwischen diesen Operatoren untersuchen.
Gemäß den ASSOCIATIVITY-REGELN dieser beiden bestimmten Operatoren beginnen wir mit der Ausführung der Gleichung von LINKS NACH RECHTS, dh 100/10 wird zuerst ausgeführt:
Schritt 3: Die Gleichung befindet sich jetzt im folgenden Ausführungszustand:
Nach den Regeln und Vorrang: + und - haben Vorrang.
Wir müssen uns nun die Assoziativität zwischen den Operatoren + und - Operatoren ansehen. Entsprechend der Assoziativität dieser beiden bestimmten Operatoren beginnen wir mit der Ausführung der Gleichung von LINKS nach RECHTS, dh 3 + 20 wird zuerst ausgeführt:
10 ist die richtige Ausgabe beim Kompilieren
Auch hier ist es wichtig, eine Tabelle mit den Regeln und der Assoziativität der Rangfolge bei sich zu haben, wenn Sie diese Fragen lösen, z. B. http://introcs.cs.princeton.edu/java/11precedence/.
quelle
10 - 4 - 3
.+
Operator (der die Assoziativität von rechts nach links hat) ist, aber additive + und - genau wie multiplikative * /% übrig haben zur richtigen Assoziativität.