Es ist keine Kurzschrift.
Das +=
Symbol erschien in den 1970er Jahren in der C-Sprache und - mit der C-Idee "Smart Assembler" korrespondiert es mit einer deutlich anderen Maschinenanweisung und Adressierungsart:
Dinge wie " i=i+1
", "i+=1
"und" ++i
"entsprechen, obwohl sie auf abstrakter Ebene den gleichen Effekt haben, auf niedriger Ebene einer anderen Arbeitsweise des Prozessors.
Insbesondere diese drei Ausdrücke, vorausgesetzt, die i
Variable befindet sich in der Speicheradresse, die in einem CPU-Register gespeichert ist (nennen wir es D
- betrachten wir es als "Zeiger auf int"), und die ALU des Prozessors nimmt einen Parameter und gibt ein Ergebnis in einem zurück "Akku" (nennen wir es A - denken Sie daran als int).
Mit diesen Einschränkungen (sehr häufig in allen Mikroprozessoren aus dieser Zeit) wird die Übersetzung höchstwahrscheinlich sein
;i = i+1;
MOV A,(D); //Move in A the content of the memory whose address is in D
ADD A, 1; //The addition of an inlined constant
MOV (D) A; //Move the result back to i (this is the '=' of the expression)
;i+=1;
ADD (D),1; //Add an inlined constant to a memory address stored value
;++i;
INC (D); //Just "tick" a memory located counter
Die erste Möglichkeit ist nicht optimal, sie ist jedoch allgemeiner, wenn Sie mit Variablen anstelle von Konstanten ( ADD A, B
oder ADD A, (D+x)
) arbeiten oder komplexere Ausdrücke übersetzen (sie laufen alle im Push-Vorgang mit niedriger Priorität in einem Stapel zusammen, nennen Sie die hohe Priorität pop und wiederholen, bis alle Argumente beseitigt sind.
Die zweite ist typischer für "Zustandsmaschine": Wir "evaluieren" nicht länger einen Ausdruck, sondern "operieren einen Wert": Wir verwenden weiterhin die ALU, vermeiden jedoch, dass Werte verschoben werden, damit das Ergebnis den Parameter ersetzen kann. Diese Art von Anweisung kann nicht verwendet werden, wenn kompliziertere Ausdrücke erforderlich sind: i = 3*i + i-2
Kann nicht an Ort und Stelle ausgeführt werden, da i
mehrmals erforderlich.
Die dritte - einfachere - betrachtet nicht einmal die Idee der "Addition", sondern verwendet eine "primitivere" (im rechnerischen Sinne) Schaltung für einen Zähler. Der Befehl ist kurzgeschlossen, wird schneller geladen und sofort ausgeführt, da das kombinatorische Netzwerk, das zum Nachrüsten eines Registers erforderlich ist, um es zu einem Zähler zu machen, kleiner und daher schneller als das eines Volladdierers ist.
Mit modernen Compilern (siehe C), die die Compileroptimierung ermöglichen, kann die Korrespondenz nach Belieben ausgetauscht werden, aber es gibt immer noch einen konzeptionellen Unterschied in der Semantik.
x += 5
meint
- Suchen Sie den mit x gekennzeichneten Ort
- Addiere 5 dazu
Aber x = x + 5
heißt:
- Bewerten Sie x + 5
- Suchen Sie den mit x gekennzeichneten Ort
- Kopieren Sie x in einen Akku
- Addiere 5 zum Akku
- Speichern Sie das Ergebnis in x
- Suchen Sie den mit x gekennzeichneten Ort
- Kopieren Sie den Akku darauf
Natürlich kann Optimierung
- Wenn "x finden" keine Nebenwirkungen hat, können die beiden "Suchen" einmal durchgeführt werden (und x wird zu einer Adresse, die in einem Zeigerregister gespeichert ist).
- Die zwei Kopien können entfernt werden, wenn der ADD
&x
stattdessen auf den Akku angewendet wird
Auf diese Weise wird der optimierte Code so gestaltet, dass er mit dem x += 5
einen übereinstimmt .
Dies ist jedoch nur möglich, wenn "x finden" keine Nebenwirkungen hat, ansonsten
*(x()) = *(x()) + 5;
und
*(x()) += 5;
sind semantisch unterschiedlich, da x()
Nebenwirkungen (zugeben x()
ist eine Funktion, die seltsame Dinge tut und zurückgibt int*
) zweimal oder einmal auftreten.
Die Äquivalenz zwischen x = x + y
und x += y
ist daher auf den besonderen Fall zurückzuführen, in dem +=
und =
auf einen direkten l-Wert angewendet werden.
Um auf Python umzusteigen, wurde die Syntax von C übernommen. Da es jedoch keine Übersetzung / Optimierung VOR der Ausführung in interpretierten Sprachen gibt, hängen die Dinge nicht unbedingt so eng zusammen (da es nur einen Analyseschritt weniger gibt). Ein Interpreter kann jedoch auf verschiedene Ausführungsroutinen für die drei Ausdruckstypen verweisen und dabei abhängig von der Art der Ausdrucksbildung und dem Auswertungskontext unterschiedlichen Maschinencode verwenden.
Für diejenigen, die mehr Details mögen ...
Jede CPU hat eine ALU (arithmetisch-logische Einheit), die im Grunde genommen ein kombinatorisches Netzwerk ist, dessen Ein- und Ausgänge in Abhängigkeit vom Operationscode des Befehls mit den Registern und / oder dem Speicher "verbunden" sind.
Binäroperationen werden typischerweise als "Modifikator eines Akkumulatorregisters" implementiert, wobei eine Eingabe "irgendwo" erfolgt, wobei sich irgendwo - innerhalb des Befehlsflusses selbst (typisch für Manifest Contant: ADD A 5) - innerhalb eines anderen Registers befinden kann (typisch für Ausdrucksberechnung mit temporaries (z. B. ADD AB) - innerhalb des Speichers an einer von einem Register vorgegebenen Adresse (typisch für das Abrufen von Daten, z. B. ADD A (H)) - H funktionieren in diesem Fall wie ein Dereferenzierungszeiger.
Mit diesem Pseudocode x += 5
ist
ADD (X) 5
während x = x+5
ist
MOVE A (X)
ADD A 5
MOVE (X) A
Das heißt, x + 5 gibt ein temporäres Objekt an, das später zugewiesen wird. x += 5
arbeitet direkt auf x.
Die tatsächliche Implementierung hängt vom tatsächlichen Befehlssatz des Prozessors ab: Wenn kein ADD (.) c
Opcode vorhanden ist , wird der erste Code zum zweiten: no way.
Wenn es einen solchen Opcode gibt und die Optimierung aktiviert ist, wird der zweite Ausdruck nach Eliminierung der Rückwärtsbewegung und Anpassung des Register-Opcodes der erste.
x += 5
alsx = x + 5
? Oder ist es wirklich nur syntaktischer Zucker, wie Sie vorschlagen?