Nie mam pojęcia, jak się one faktycznie nazywają, ale cały czas je widzę. Implementacja Pythona wygląda mniej więcej tak:
x += 5
jako skrótowy zapis dla x = x + 5
.
Ale dlaczego jest to uważane za dobrą praktykę? Natknąłem się na to w prawie każdej książce lub samouczku programowania, który przeczytałem dla Python, C, R i tak dalej. Rozumiem, że to wygodne, oszczędzając trzy naciśnięcia klawiszy, w tym spacje. Ale zawsze wydają mi się denerwować, gdy czytam kod, a przynajmniej w mojej opinii sprawiają, że jest mniej czytelny, a nie więcej.
Czy brakuje mi jasnego i oczywistego powodu, że są one używane wszędzie?
programming-practices
Fomite
źródło
źródło
x += 5
niż powiedziećx = x + 5
? A może to tak naprawdę cukier syntaktyczny, jak sugerujesz?Odpowiedzi:
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
Pierwszy sposób na zrobienie tego jest nieoptymalny, ale jest bardziej ogólny, gdy używa się zmiennych zamiast stałej (
ADD A, B
lubADD 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
znaczyAle
x = x + 5
oznacza:Oczywiście optymalizacja może
&x
zamiast ADD zastosowano akumulatordzię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
i
są semantycznie różne, ponieważ
x()
efekty uboczne (przyznawanie sięx()
jest funkcją robienia dziwnych rzeczy i zwracanie anint*
) będą wywoływane dwa lub jeden raz.Równoważność między
x = x + y
ix += 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
jestpóki
x = x+5
jestOznacza 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.
źródło
W zależności od tego, jak o tym myślisz, jest to łatwiejsze do zrozumienia, ponieważ jest prostsze. Weź na przykład:
x = x + 5
wywołuje przetwarzanie w myślach „weź x, dodaj do niego pięć, a następnie przypisz tę nową wartość z powrotem do x”x += 5
można uznać za „zwiększenie x o 5”Nie jest to więc tylko skrót, w rzeczywistości opisuje funkcjonalność znacznie bardziej bezpośrednio. Podczas czytania fragmentów kodu łatwiej jest zrozumieć.
źródło
x = x + 5
ciągle mnie niepokoił. Kiedy później zacząłem matematykę, jeszcze bardziej mnie to niepokoiło. Używaniex += 5
jest znacznie bardziej opisowe i ma znacznie większy sens jako wyrażenie.reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1
... och nie !!! literówkaPrzynajmniej w Pythonie ,
x += y
ax = x + y
może zrobić całkowicie różne rzeczy.Na przykład, jeśli to zrobimy
następnie
a += [3]
spowodujea == b == [3]
, natomiasta = a + [3]
spowodujea == [3]
ab == []
. Oznacza to, że+=
modyfikuje obiekt w miejscu (cóż, może to zrobić, możesz zdefiniować__iadd__
metodę wykonywania praktycznie wszystkiego, co chcesz), a jednocześnie=
tworzy nowy obiekt i wiąże z nim zmienną.Jest to bardzo ważne podczas wykonywania pracy numerycznej z NumPy , ponieważ często kończy się to wieloma referencjami do różnych części tablicy, i ważne jest, aby upewnić się, że nie zmodyfikujesz przypadkowo części tablicy, do której istnieją inne odniesienia, lub niepotrzebnie kopiuj tablice (które mogą być bardzo drogie).
źródło
__iadd__
: istnieją języki, w którym można utworzyć odwołanie do niezmiennej zmienny datastructure, gdzieoperator +=
jest zdefiniowana, np scala:val sb = StringBuffer(); lb += "mutable structure"
vsvar s = ""; s += "mutable variable"
. tym bardziej modyfikuje zawartość struktury danych, podczas gdy ta ostatnia powoduje, że zmienna wskazuje na nową.Nazywa się to idiomem . Idiomy programowania są użyteczne, ponieważ są spójnym sposobem pisania określonej konstrukcji programowania.
Ilekroć ktoś pisze
x += y
, wiesz, żex
jest on zwiększany przezy
nie bardziej skomplikowaną operację (jako najlepsza praktyka, zazwyczaj nie mieszałbym bardziej skomplikowanych operacji i tych skrótów składniowych). Ma to największy sens przy zwiększaniu o 1.źródło
++x
ix+=1
są równoważne w C i Javie (być może także w C #), choć niekoniecznie w C ++ ze względu na złożoną semantykę operatora. Kluczem jest to, że obaj oceniająx
raz, zwiększają zmienną o jeden i uzyskują wynik, który jest zawartością zmiennej po ocenie.++x + 3
nie jest taki sam jakx += 1 + 3
. Nawiasujx += 1
i jest identyczny. Same wypowiedzi są identyczne.++x + 3
,x += 1 + 3
lub(x += 1) + 3
mają niezdefiniowane zachowanie (zakładając Uzyskana wartość „leży”).Aby nieco wyjaśnić punkt @ Pubby, zastanów się
someObj.foo.bar.func(x, y, z).baz += 5
Bez
+=
operatora istnieją dwa sposoby:someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5
. Jest to nie tylko strasznie zbędne i długie, ale także wolniejsze. Dlatego trzeba by byłotmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5
. To jest w porządku, ale jest prosty hałas. To jest naprawdę bardzo zbliżone do tego, co dzieje się w środowisku+=
uruchomieniowym , ale pisanie jest uciążliwe, a samo użycie spowoduje przeniesienie pracy do kompilatora / interpretera.Zalety
+=
i inne takie podmioty są niezaprzeczalne, a przyzwyczajenie się do nich to tylko kwestia czasu.źródło
To prawda, że jest krótszy i łatwiejszy, i prawdą jest, że prawdopodobnie został zainspirowany podstawowym językiem asemblera, ale najlepszą praktyką jest to, że zapobiega całej klasie błędów i ułatwia przeglądanie kodu i pewność co to robi.
Z
Ponieważ w grę wchodzi tylko jedna nazwa zmiennej, jesteś pewien, co robi instrukcja.
Z
RidiculouslyComplexName = RidiculosulyComplexName + 1;
Zawsze są wątpliwości, że obie strony są dokładnie takie same. Widziałeś błąd? Gorzej, gdy indeksy dolne i kwalifikatory są obecne.
źródło
Chociaż notacja + = jest idiomatyczna i krótsza, nie są to powody, dla których łatwiej ją odczytać. Najważniejszą częścią czytania kodu jest mapowanie składni na znaczenie, a więc im bliższa składnia pasuje do procesów myślowych programisty, tym bardziej będzie ona czytelna (jest to również powód, dla którego kod płytki wzorcowej jest zły: nie jest częścią myśli proces, ale nadal konieczne, aby funkcja kodu działała). W tym przypadku chodzi o „zmienną przyrostową x o 5”, a nie „niech x będzie wartością x plus 5”.
Istnieją inne przypadki, w których krótsza notacja jest niekorzystna dla czytelności, na przykład gdy używasz operatora trójskładnikowego, w którym
if
instrukcja byłaby bardziej odpowiednia.źródło
Aby dowiedzieć się, dlaczego operatorzy ci posługują się językami w stylu „C”, można znaleźć fragment K&R 1st Edition (1978) 34 lata temu:
Myślę, że z tego fragmentu jasno wynika, że Brian Kernighan i Dennis Ritchie (K&R) wierzyli, że operatorzy przypisań złożonych pomogli w odczytaniu kodu.
Minęło dużo czasu, odkąd K&R to napisał, a wiele „najlepszych praktyk” na temat tego, jak ludzie powinni pisać kod, zmieniło się lub ewoluowało. Ale to pytanie dla programistów.stackexchange po raz pierwszy przypominam sobie kogoś, kto wyraża skargę na czytelność przypisań złożonych, więc zastanawiam się, czy wielu programistów uważa je za problem? Z drugiej strony, kiedy piszę to pytanie, ma 95 głosów pozytywnych, więc może ludzie uważają je za zgrzytliwe podczas czytania kodu.
źródło
Oprócz czytelności, w rzeczywistości robią różne rzeczy:
+=
nie muszą dwukrotnie oceniać swojego lewego operandu.Na przykład
expr = expr + 5
ewaluowałbyexpr
dwukrotnie (zakładając, żeexpr
jest nieczysty).źródło
expr = expr + 5
iexpr += 5
expr
ma skutki uboczne.expr
ma skutki uboczne,expr = expr + 5
należy wywołać te efekty dwa razy.volatile
są skutkami ubocznymix=x+5
ix+=5
mają takie same skutki uboczne, kiedyx
jestvolatile
Oprócz oczywistych zalet, które inni opisali bardzo dobrze, kiedy masz bardzo długie nazwiska, jest bardziej zwarty.
lub
źródło
Jest zwięzłe.
Pisanie jest znacznie krótsze. Obejmuje mniej operatorów. Ma mniejszą powierzchnię i mniejszą szansę na zamieszanie.
Korzysta z bardziej konkretnego operatora.
To wymyślony przykład i nie jestem pewien, czy faktyczne kompilatory to implementują. x + = y faktycznie używa jednego argumentu i jednego operatora i modyfikuje x w miejscu. x = x + y może mieć pośrednią reprezentację x = z, gdzie z oznacza x + y. Ten ostatni używa dwóch operatorów, dodawania i przypisania oraz zmiennej tymczasowej. Pojedynczy operator wyjaśnia, że strona wartości nie może być niczym innym niż y i nie musi być interpretowana. I teoretycznie może istnieć jakiś fantazyjny procesor, który ma operator plus-equals, który działa szybciej niż operator plus i operator przypisania szeregowo.
źródło
ADD
instrukcje na wielu procesorach mieć warianty, które działają bezpośrednio na rejestrach lub pamięci z wykorzystaniem innych rejestrów, pamięci lub stałe jako drugi Dobudowa. Nie wszystkie kombinacje są dostępne (np. Dodaj pamięć do pamięci), ale wystarczy, aby być użytecznym. W każdym razie każdy kompilator z przyzwoitym optymalizatorem będzie wiedział, jak wygenerować taki sam kod,x = x + y
jak by to zrobiłx += y
.To miły idiom. To, czy jest szybsze, czy nie, zależy od języka. W C jest to szybsze, ponieważ przekłada się na instrukcję zwiększenia zmiennej po prawej stronie. Nowoczesne języki, w tym Python, Ruby, C, C ++ i Java, wszystkie obsługują składnię op =. Jest kompaktowy i szybko się przyzwyczajasz. Ponieważ zobaczysz go bardzo często w kodzie innych osób (OPC), możesz równie dobrze się do niego przyzwyczaić i go używać. Oto, co dzieje się w kilku innych językach.
W Pythonie pisanie
x += 5
nadal powoduje utworzenie obiektu liczb całkowitych 1 (chociaż można go wyciągnąć z puli) i osierocenie obiektu liczb całkowitych zawierających 5.W Javie powoduje ukrytą rzutowanie. Spróbuj pisać
źródło
x+=5
ma tak małe znaczenie jakx=x+5
; na przykład w Haskell ten ostatni nie powodujex
zwiększenia o 5 - zamiast tego powoduje nieskończoną pętlę rekurencyjną. Kto by tego chciał?+=
zależy nie tyle od języka, ile od procesora . Na przykład X86 jest architekturą dwóch adresów, obsługuje tylko+=
natywnie. Instrukcja takaa = b + c;
musi zostać skompilowana,a = b; a += c;
ponieważ po prostu nie ma instrukcji, która umieściłaby wynik dodania w innym miejscu niż miejsce jednego z zestawień. Natomiast architektura Power jest architekturą z trzema adresami, dla której nie ma specjalnych poleceń+=
. W tej architekturze instrukcjea += b;
ia = a + b;
zawsze kompilują się do tego samego kodu.Operatory, takie jak,
+=
są bardzo przydatne, gdy używasz zmiennej jako akumulatora , tj. Bieżącej sumy:Jest o wiele łatwiejszy do odczytania niż:
W pierwszym przypadku koncepcyjnie modyfikujesz wartość w
x
. W drugim przypadku obliczasz nową wartość i przypisujesz jąx
każdorazowo. I chociaż prawdopodobnie nigdy nie napisałbyś tak prostego kodu, pomysł pozostaje ten sam ... nacisk kładziony jest na to, co robisz, na istniejącą wartość zamiast tworzenia nowej wartości.źródło
Rozważ to
D0 naprawdę chcesz pisać
źródło
Inne odpowiedzi dotyczą bardziej powszechnych przypadków, ale jest też inny powód: w niektórych językach programowania może być przeciążony; np . Scala .
Mała lekcja Scala:
Jeśli klasa definiuje tylko
+
operator,x+=y
to rzeczywiście jest skrótem odx=x+y
.Jeśli klasa zostanie przeciążona
+=
, nie są to:Dodatkowo operatory
+
i+=
są dwoma oddzielnymi operatorami (i nie tylko: + a, ++ a, a ++, a + ba + = b są również różnymi operatorami); w językach, w których dostępne jest przeciążenie operatora, może to powodować interesujące sytuacje. Tak jak opisano powyżej - jeśli przeciążasz+
operatora, aby wykonać dodawanie, pamiętaj, że również+=
będzie to konieczne.źródło
+=
a następnie definiujea+b
jako „wykonaj kopięa
, włóżb
ją za pomocą+=
, zwróć kopięa
”.Powiedz to raz i tylko raz: w
x = x + 1
, mówię dwa razy „x”.Ale nigdy nie pisz: „a = b + = 1”, inaczej będziemy musieli zabić 10 kociąt, 27 myszy, psa i chomika.
Nigdy nie powinieneś zmieniać wartości zmiennej, ponieważ ułatwia to udowodnienie poprawności kodu - patrz programowanie funkcjonalne. Jednak jeśli to zrobisz, lepiej nie mówić rzeczy tylko raz.
źródło