To nie jest stenografia.
+=
Symbol pojawił się w języku C w 1970 roku, a także - z ideą C „inteligentnego asemblerze” odpowiadają wyraźnie inny tryb obsługi maszyn i adresowania:
Rzeczy takie jak „ i=i+1
”, "i+=1
„i” ++i
”, choć na poziomie abstrakcyjnym dają ten sam efekt, na niskim poziomie odpowiadają innym sposobom działania procesora.
W szczególności te trzy wyrażenia, zakładając, że i
zmienna znajduje się w adresie pamięci przechowywanym w rejestrze procesora (nazwijmy ją D
- pomyśl o tym jako o „wskaźniku do int”), a ALU procesora przyjmuje parametr i zwraca wynik w postaci „accumulator” (nazwijmy to A - pomyśl o tym jako int).
Przy tych ograniczeniach (bardzo powszechnych we wszystkich mikroprocesorach z tego okresu) tłumaczenie najprawdopodobniej będzie
;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
Pierwszy sposób na zrobienie tego jest nieoptymalny, ale jest bardziej ogólny, gdy używa się zmiennych zamiast stałej ( ADD A, B
lub ADD A, (D+x)
) lub tłumaczy bardziej złożone wyrażenia (wszystkie sprowadzają się do operacji push o niskim priorytecie na stosie, nazywają wysoki priorytet, pop i powtarzaj, aż wszystkie argumenty zostaną wyeliminowane).
Drugi jest bardziej typowy dla „automatu stanów”: już nie „oceniamy wyrażenia”, ale „operujemy wartością”: nadal używamy ALU, ale unikamy przesuwania wartości, ponieważ wynik może zastąpić parametr. Tego rodzaju instrukcji nie można stosować tam, gdzie wymagane są bardziej skomplikowane wyrażenia: i = 3*i + i-2
nie można ich obsługiwać w miejscu, ponieważ i
jest to wymagane więcej razy.
Trzeci - nawet prostszy - nawet nie bierze pod uwagę idei „dodawania”, ale używa bardziej „prymitywnego” (w sensie obliczeniowym) obwodu dla licznika. Instrukcja jest zwarta, ładuje się szybciej i wykonuje się natychmiast, ponieważ sieć kombinatoryczna wymagana do doposażenia rejestru, aby był licznikiem, jest mniejsza, a zatem szybsza niż ta z sumatorem pełnym.
We współczesnych kompilatorach (już teraz C), umożliwiających optymalizację kompilatora, korespondencję można zamieniać w zależności od wygody, ale nadal istnieje koncepcyjna różnica w semantyce.
x += 5
znaczy
- Znajdź miejsce oznaczone x
- Dodaj do tego 5
Ale x = x + 5
oznacza:
- Oceń x + 5
- Znajdź miejsce oznaczone x
- Skopiuj x do akumulatora
- Dodaj 5 do akumulatora
- Zapisz wynik w x
- Znajdź miejsce oznaczone x
- Skopiuj do niego akumulator
Oczywiście optymalizacja może
- jeśli „znalezienie x” nie ma skutków ubocznych, dwa „znalezienie” można wykonać raz (i x stać się adresem zapisanym w rejestrze wskaźnika)
- dwie kopie można pominąć, jeśli
&x
zamiast ADD zastosowano akumulator
dzięki czemu zoptymalizowany kod jest zgodny z tym x += 5
samym.
Ale można to zrobić tylko wtedy, gdy „znalezienie x” nie ma skutków ubocznych, w przeciwnym razie
*(x()) = *(x()) + 5;
i
*(x()) += 5;
są semantycznie różne, ponieważ x()
efekty uboczne (przyznawanie się x()
jest funkcją robienia dziwnych rzeczy i zwracanie an int*
) będą wywoływane dwa lub jeden raz.
Równoważność między x = x + y
i x += y
wynika stąd ze szczególnego przypadku, w którym +=
i =
są stosowane do bezpośredniej wartości l.
Aby przejść do Pythona, odziedziczył składnię z C, ale ponieważ nie ma tłumaczenia / optymalizacji PRZED wykonaniem w językach interpretowanych, rzeczy niekoniecznie są tak ściśle ze sobą powiązane (ponieważ jest o jeden krok mniej analizy). Jednak interpreter może odwoływać się do różnych procedur wykonywania dla trzech typów wyrażeń, wykorzystując inny kod maszynowy w zależności od sposobu utworzenia wyrażenia i kontekstu oceny.
Dla tych, którzy lubią więcej szczegółów ...
Każdy procesor ma ALU (jednostkę arytmetyczno-logiczną), która w swej istocie jest siecią kombinatoryczną, której wejścia i wyjścia są „podłączane” do rejestrów i / lub pamięci w zależności od kodu operacji instrukcji.
Operacje binarne są zwykle realizowane jako „modyfikator rejestru akumulatorów z wejściem pobranym„ gdzieś ”, gdzie gdzieś może być - wewnątrz samego przepływu instrukcji (typowy dla manifestu: ADD A 5) - wewnątrz innego rejestru (typowy dla obliczeń wyrażeń z tymczasowe: np. ADD AB) - w pamięci, pod adresem podanym przez rejestr (typowe dla pobierania danych, np .: ADD A (H)) - H, w tym przypadku działa jak wskaźnik dereferencji.
Z tym pseudokodem x += 5
jest
ADD (X) 5
póki x = x+5
jest
MOVE A (X)
ADD A 5
MOVE (X) A
Oznacza to, że x + 5 daje wartość tymczasową, która jest później przypisywana. x += 5
działa bezpośrednio na x.
Rzeczywista implementacja zależy od rzeczywistego zestawu instrukcji procesora: Jeśli nie ma ADD (.) c
kodu operacji, pierwszy kod staje się drugim: nie ma mowy.
Jeśli istnieje taki kod operacji, a optymalizacja jest włączona, drugie wyrażenie, po wyeliminowaniu odwrotnych ruchów i dostosowaniu kodu operacji rejestru, staje się pierwszym.
x += 5
niż powiedziećx = x + 5
? A może to tak naprawdę cukier syntaktyczny, jak sugerujesz?