Dlaczego operator przypisania przypisuje do lewej strony?

47

Niedawno zacząłem uczyć znajomego programowania (używamy Pythona), a kiedy zaczęliśmy omawiać tworzenie zmiennych i operatora przypisania, zapytała, dlaczego wartość po prawej stronie jest przypisana do nazwy po lewej stronie, a nie odwrotnie .

Nie myślałem o tym zbyt wiele, ponieważ wydawało mi się to naturalne, ale powiedziała, że ​​od lewej do prawej wydaje się jej bardziej naturalna, ponieważ tak większość z nas czyta naturalne języki.

Pomyślałem o tym i doszedłem do wniosku, że znacznie ułatwia odczytanie kodu, ponieważ nazwy, które są przypisane (do których programista będzie musiał ponownie użyć), są dobrze widoczne, wyrównane po lewej stronie.

aligned = 2
on = 'foo' + 'bar' + 'foobar'
the = 5.0 / 2
left = 2 + 5

W przeciwieństwie do:

2 = aligned 
'foo' + 'bar' + 'foobar' = on
5.0 / 2 = the 
2 + 5 = right 

# What were the names again...?

Teraz zastanawiam się, czy istnieją również inne powody tego standardu. Czy kryje się za tym historia? A może jest jakiś techniczny powód, dla którego jest to dobra opcja (niewiele wiem o kompilatorach)? I czy są jakieś języki programowania, które przypisują po prawej stronie?

voithos
źródło
15
R można przypisać do prawej strony ( value -> variable).
Ty
Być może jest to spowodowane złymi nazwami zmiennych? Co powiesz na „numAligned is 2” i „chickensLeft is 2 + 5”?
Job
18
Czy twoje imię ma na imię ... Yoda?
Adrian
1
Jeśli zacznie się nad tym zastanawiać, zastanawiam się, co zrobi, gdy zobaczy C ++ & co.
BlackBear,
3
Na marginesie: Khan Academy ma lekcje na temat Pythona dla początkujących programistów: khanacademy.org/#computer-science, a Google ma dla bardziej zaawansowanych: code.google.com/edu/languages/google-python-class/set- up.html
lindon fox

Odpowiedzi:

56

Ditto @paxdiablo. Języki wczesnego programowania zostały napisane przez matematyków - właściwie wszystkie były. W matematyce, według jej własnej zasady - czytania od lewej do prawej - ma sens sposób, w jaki działa.

x = 2–4.

W matematyce powiedziałbyś: Niech x będzie równe 2y -4.

Robisz to nawet w algebrze. Kiedy rozwiązujesz równanie dla zmiennej, izolujesz zmienną, dla której rozwiązujesz, po lewej stronie. tj. y = mx + b;

Ponadto, gdy cała rodzina języków - na przykład rodzina C - ma określoną składnię, zmiana jest bardziej kosztowna.

Jonathan Henson
źródło
19
@FarmBoy: w matematyce przypisanie i równość są tym samym, ponieważ w formule nie ma sekwencji jak w komputerach. (a równa się b, a jednocześnie b równa się a)
Petruza
1
@FB z wyjątkiem niektórych języków funkcjonalnych z pojedynczym przypisaniem, takich jak np. Erlang. Przydział i zapewnienie równości są takie same jak w matematyce
Peer Stritzinger
18
@Petruza Nie, w matematyce zadania i równość to nie to samo. Jeśli powiem „Niech x = 2y - 3” różni się od „Zatem x = 2y - 3”. Matematyka, zazwyczaj różnicuje je kontekst. Ponieważ komentarz, który mnie kwestionuje, został tak powszechnie przyjęty, wspomnę, że mam doktorat. w matematyce jestem tego pewien.
Eric Wilson,
2
Nie znam matematyki nigdzie w pobliżu doktoratu, twierdzę, że ponieważ nie ma sekwencyjności, nie ma kolejności wykonywania w matematyce zarówno w zadaniu, jak i w równości, w przeciwieństwie do programowania, w którym obie strony zadania mogą być różnią się w pewnym momencie i ostatecznie stają się równe w innym momencie. Ale w matematyce, w zadaniu takim, let a be...jak nie ma czasu, obie strony zadania są równe, więc zadanie jest w rzeczywistości równością, nic dziwnego, dlaczego oba używają tego samego znaku:=
Petruza
5
@Petruzza - ale jest sekwencyjność. Dokumenty matematyczne są zapisywane od początku do końca, tak samo jak każdy inny dokument. Jeśli twierdzę x = 1w rozdziale pierwszym, ale stwierdzam x = 2w rozdziale drugim, nie jest to żadna straszna sprzeczność - każde stwierdzenie ma zastosowanie tylko w określonym kontekście. Różnica w programowaniu imperatywnym polega częściowo na usunięciu bariery (nie potrzebujemy zmiany kontekstu), a częściowo na implementacji i użyteczności.
Steve314,
26

BASIC, jeden z najwcześniejszych języków komputerowych miał „właściwą” formę:

10 LET AREA = HEIGHT * WIDTH

co odpowiada matematycznemu sposobowi określania zmiennej, na przykład „Niech H będzie wysokością obiektu”.

COBOLbył również podobny ze swoim COMPUTEoświadczeniem. Podobnie jak w przypadku wielu sposobów działania, może to być po prostu arbitralna decyzja podjęta w wielu językach.


źródło
7
Przypuszczam, że ta forma jest bardziej naturalna, gdy wiersze programu są uważane za „instrukcje” w przeciwieństwie do „operacji”. Jak w, I declare that X must equal THISzamiast bardziej liniowegoEvaluate THIS and store it in X
voithos
Zaraz, BASIC był „wczesnym” językiem komputerowym?
Alex Feinman,
2
@Alex Biorąc pod uwagę, że nawiązuje do lat 60., powiedziałbym, że to dość wcześnie.
Ben Richards
1
Z drugiej strony, w języku COBOL można pisać MULTIPLY HEIGHT BY WIDTH GIVING AREA, więc zmienna, która otrzymuje wynik, znajduje się po prawej stronie instrukcji.
user281377,
25

W rzeczywistości istnieje język programowania, który przypisuje się do prawej strony: TI-BASIC ! Nie tylko to, ale również nie używa „=” jako operatora przypisania, ale raczej używa strzałki znanej jako operator „STO”.

przykłady:

5→A
(A + 3)→B
(A - B)→C

W powyższym przykładzie deklarowane są trzy zmienne i podane wartości. A byłoby 5, B byłoby 8, a C byłoby -3. Pierwszą deklarację / przypisanie można odczytać „zapisz 5 jako A”.

Jeśli chodzi o to, dlaczego TI-BASIC używa takiego systemu do przypisywania, przypisuję go byciu, ponieważ jest to język programowania kalkulatora. Operator „STO” w kalkulatorach TI był najczęściej używany w normalnych operacjach kalkulatora po obliczeniu liczby. Gdyby to był numer, który użytkownik chciał zapamiętać, nacisnąłby przycisk „STO”, a kaclulator poprosiłby go o nazwę (automatycznie włącza blokadę alfa, aby naciśnięcia klawiszy tworzyły litery zamiast cyfr):

Sin(7 + Cos(3))
                    -.26979276
Ans→{variable name}
                    -.26979276

a użytkownik może nazwać zmienną, co wybierze. Konieczność włączenia blokady alfa, wpisz nazwę, a następnie naciśnij „STO”, a naciśnięcie klawisza „Ans” byłoby zbyt skomplikowane dla normalnych operacji. Ponieważ wszystkie funkcje kalkulatora są dostępne w TI-BASIC, nie dodano żadnych innych operatorów przypisania, ponieważ „STO” wykonał to samo zadanie, choć wstecz w porównaniu do większości innych języków.

(Anegdota: TI-BASIC był jednym z pierwszych języków, których się nauczyłem, więc kiedy pierwszy raz uczyłem się języka Java na studiach, czułem się tak, jakby przypisanie do LEFT było niezwykłe i „zacofane”!)

diceguyd30
źródło
+1 Całkowicie o tym zapomniałem! TI Basic był również moim pierwszym językiem, ale nie pamiętam tego szczegółu.
barjak
W rzeczywistości operator STO jest bliżej tego, jak działa maszyna i jak działa każdy język. Wartość jest najpierw obliczana, a następnie zapisywana w pamięci.
Kratz
15

Heurystyka 1: W obliczu więcej niż jednego możliwego sposobu zrobienia czegoś podczas projektowania języka, wybierz najczęstszy, najbardziej intuicyjny, inaczej skończysz z Perl +.

Teraz, jak jest to bardziej naturalne (przynajmniej językiem angielskim)? Zobaczmy, jak piszemy / mówimy po angielsku:

Steven ma teraz 10 lat (w przeciwieństwie do 10 lat, kiedy Steven ma teraz). Ważę ponad 190 funtów (w przeciwieństwie do ponad 190 funtów ważę).

W kodzie:

steven = 10
i > 190

Brzmi również bardziej naturalnie:

„Jeśli Mary ma 18 lat, może mieć cukierki”. „Jeśli mam mniej niż 21 lat, poproszę mojego brata o tequilę”.

if (mary == 18) { ... }
if (i < 21) { ... }

niż:

„Jeśli 18 lat Maria ma ...” „Jeśli 21 lat jest więcej niż mój wiek ...”

Teraz kod:

if (18 == mary) { ... }
if (21 > i) { ... }

Pamiętaj, że nie jest to naturalne ani dla programistów, ani dla osób mówiących po angielsku. Zdania brzmią jak yoda-speak, a kod jest nazywany warunkami yoda. Mogą to być pomocne w C ++, ale jestem pewien, że większość ludzi się zgodzi: jeśli kompilator mógłby wykonać ciężkie podnoszenie i zmniejszyć potrzebę warunków yoda, życie byłoby nieco łatwiejsze.

Oczywiście można się przyzwyczaić do wszystkiego. Na przykład liczba 81 jest zapisana jako:

Osiemdziesiąt jeden (angielski) Osiemdziesiąt jeden (hiszpański) Jeden i osiemdziesiąt (niemiecki).

Wreszcie są 4! = 24 poprawne sposoby powiedzenia „zielone jabłko leży na stole” w języku rosyjskim - kolejność (prawie) nie ma znaczenia, z wyjątkiem tego, że „on” musi łączyć się z „table”. Tak więc, jeśli jesteś rodzimym językiem rosyjskim (na przykład), możesz nie mieć znaczenia, czy ktoś pisze a = 10lub 10 = aponieważ oba wydają się równie naturalne.

Podczas gdy językoznawstwo jest fascynującym przedmiotem, nigdy formalnie go nie studiowałem i nie znam tak wielu języków. Mam nadzieję, że podałem jednak wystarczającą liczbę kontrprzykładów.

Praca
źródło
4
... a po francusku 81 jest powiedziane jako „cztery razy dwadzieścia jeden” ... :)
Martin Sojka
1
@Martin, uważam, że duński jest bardzo podobny.
CVn
7
@Martin To naprawdę dziwne, bo cztery razy dwadzieścia jeden to 84.
Peter Olson
6
@Peter: źle wpisałeś nawias, to(four times twenty) one
SingleNegationElimination
4
@Job: Czytając twoje „Jeśli 18-letnia Mary ma ...”, nieuchronnie przypomniałem sobie Yodę mówiącego: „Kiedy osiągniesz dziewięćset lat, wyglądaj tak dobrze, że nie, hmm?” w Return of the Jedi :-)
joriki,
11

Zaczęło się od FORTRAN w latach 50. Gdzie FORTRAN był skrótem FORmula TRANslacja - omawiane wzory to proste równania algebraiczne, które zgodnie z konwencją zawsze przypisują lewą.

Z drugiej strony jego prawie współczesny język COBOL miał być podobny do angielskiego i przypisany do prawej (głównie!).

MOVE 1 TO COUNTER.
ADD +1 TO LINE-CNT.
MULTIPLY QTY BY PRICE GIVING ITEM-PRICE.
James Anderson
źródło
Myślę, że jest to najlepszy przykład tutaj, ponieważ doskonale ilustruje kontekst w języku, w którym jest czytelny dla zwykłego anglojęzycznego, ale ma właściwe zadanie. Mówię, że jest to ważne ze względu na dużą liczbę osób, które mówią po angielsku, że przeczytanie tekstu od lewej do prawej strony miałoby sens, co absolutnie nie jest prawdą.
Joshua Hedges
5

Cóż, jak zauważył @ diceguyd30, istnieją obie notacje.

  • <Identifier> = <Value>oznacza „niech identyfikator będzie wartością ”. Lub rozwinąć to: Zdefiniuj (lub ponownie zdefiniuj) zmienną Identyfikator na wartość .
  • <Value> -> <Identifier>oznacza „zapisz wartość do identyfikatora ”. Lub rozwinąć to: Wprowadź wartość do lokalizacji wyznaczonej przez identyfikator .

Oczywiście, ogólnie mówiąc, identyfikator może w rzeczywistości mieć dowolną wartość L.

Pierwsze podejście uwzględnia abstrakcyjną koncepcję zmiennych, drugie podejście dotyczy bardziej faktycznego przechowywania.

Zauważ, że pierwsze podejście jest również powszechne w językach, które nie mają przypisań. Zauważ też, że definicja i przypisanie zmiennej są względnie zbliżone <Type> <Identifier> = <Value>do siebie <Identifier> = <Value>.

back2dos
źródło
3

Jak już wspomniano, całkiem dobrze działały wszystkie wczesne języki komputerowe. Np. FORTRAN, który pojawił się wiele lat przed BASIC.

W rzeczywistości sensowne jest umieszczenie przypisanej zmiennej po lewej stronie wyrażenia przypisania. W niektórych językach możesz mieć kilka różnych przeciążonych procedur z SAMĄ NAZWĄ, zwracając różne typy wyników. Pozwalając kompilatorowi najpierw zobaczyć typ przypisanej zmiennej, wie, którą przeciążoną procedurę wywołać lub jakie niejawne rzutowanie wygenerować podczas konwersji (np.) Liczby całkowitej na zmiennoprzecinkową. To trochę uproszczone wyjaśnienie, ale mam nadzieję, że masz pomysł.

Dave Jewell
źródło
2
Rozumiem twoje wyjaśnienie, ale czy kompilator nie mógł po prostu patrzeć przed siebie do końca instrukcji?
voithos,
To może uprościć leksykon w dzisiejszych językach, ale ile języków programowania obsługuje nawet nazwane metody, nie mówiąc już o przeciążeniu metod, gdy taka składnia jest nowa?
CVn
2
Cześć @voithos. Tak - kompilator mógł patrzeć w przyszłość, ale prawdopodobnie byłby to niedopuszczalny poziom złożoności na początku pisania kompilatora - który często był ręcznie asemblerem! Myślę, że umieszczenie przypisanej zmiennej po lewej stronie jest pragmatycznym wyborem: łatwiej jest analizować zarówno człowieka, jak i maszynę.
Dave Jewell
Myślę, że przypisanie zadania po prawej stronie byłoby banalne. Gdy wyrażenie takie jak 3 + 4 == 6 + 7, obie strony są oceniane przed operatorem, ponieważ język jest definiowany rekurencyjnie. Element językowy „zmienna = wyrażenie”, można łatwo zmienić na „wyrażenie = zmienna”. To, czy spowoduje to niejednoznaczne sytuacje, zależy od reszty języka.
Kratz
@Kratz - z pewnością dotyczy to teraz kompilatorów, ale może występować niewielki problem w przypadku bardzo starych interpretowanych języków, które działały ze źródłami tokenizowanymi. OTOH, który mógł faworyzować zmienną po prawej stronie zamiast zmiennej po lewej stronie.
Steve314,
3

Może to być pozostałość po wczesnych algorytmach parsowania. Pamiętaj, że parsowanie LR zostało wynalezione dopiero w 1965 roku i może być tak, że parsery LL miały problemy (w ograniczeniach czasowych i przestrzennych maszyn w tym czasie) idące w drugą stronę. Rozważać:

identifier = function();
function();

Oba są wyraźnie jednoznaczne od drugiego tokena. Z drugiej strony,

function() = identifier;
function();

Nie śmieszne. Gorzej, gdy zaczniesz zagnieżdżać wyrażenia przypisania.

function(prev_identifier = expression) = identifier;
function(prev_identifier = expression);

Oczywiście łatwiejsze do jednoznacznego określenia dla maszyn oznacza również łatwiejsze do jednoznacznego określenia dla ludzi. Innym łatwym przykładem byłoby poszukiwanie inicjalizacji dowolnego identyfikatora.

identifier1 = expressionOfAnArbitraryLength;
identifier2 = expressionOfAReallyReallyReallyArbitraryLength;
identifier3 = expression;
identifier4 = AlongLineExpressionWithAFunctionCallWithAssignment(
    identifier = expr);

Łatwo, wystarczy spojrzeć w lewą stronę. Z drugiej strony prawa strona

expressionOfAnArbitraryLength = identifier1;
expressionOfAReallyReallyReallyArbitraryLength = identifier2;
expression = identifier3;
AlongLineExpressionWithAFunctionCallWithAssignment(expr = identifier
    ) = identifier4;

Zwłaszcza, gdy nie możesz grepdziurkować kart, znacznie trudniej jest znaleźć odpowiedni identyfikator.

DeadMG
źródło
Właśnie o tym pomyślałem, tak.
voithos
2

Języki asemblera mają miejsce docelowe jako część kodu operacji po lewej stronie. Języki wyższego poziomu były zgodne z konwencjami języków poprzedników.

Kiedy zobaczysz =(lub :=w przypadku dialektu pascalskiego), możesz je wymówić jako is assigned the value, wtedy natura od lewej do prawej będzie miała sens (ponieważ czytamy również od lewej do prawej w większości języków). Ponieważ języki programowania zostały opracowane głównie przez ludzi, którzy czytają od lewej do prawej, konwencje utknęły w miejscu.

Jest to rodzaj zależności ścieżki . Podejrzewam, że jeśli programy komputerowe zostały wymyślone przez ludzi, którzy mówili po hebrajsku lub arabsku (lub w innym języku pisanym od prawej do lewej), podejrzewam, że wybraliśmy miejsce docelowe po prawej stronie.

Tangurena
źródło
Tak, ale podejrzewam, że tekst w edytorach również będzie wyrównany do prawej ...
voithos
8
Nie można tak uogólniać na temat języków asemblerowych. Różnią się w zależności od miejsca docelowego operandu.
szybko_now
2
@quickly_now: right; w rzeczywistości większość prymitywnych języków maszynowych (nawet asemblery według dzisiejszych standardów) nawet nie miała przeznaczenia, ponieważ zwykle był tylko jeden lub dwa akumulatory ogólnego przeznaczenia. większość operacji wskazywała akumulator jako miejsce docelowe, z wyjątkiem kodów „przechowujących”, które określały tylko adres pamięci, a nie źródło (którym był akumulator). Naprawdę nie sądzę, żeby miało to jakikolwiek wpływ na składnię przypisań dla języków podobnych do ALGOL.
Javier
3
@Tangurena - Niektóre języki asemblera mają miejsce docelowe po lewej stronie. Nie opcode (to złożony kod obiektowy), ale lewa lista argumentów po lewej stronie instrukcji mnemonicznej. Jednak inni mają cel po prawej stronie. W asemblerze 68000 piszesz mov.b #255, d0na przykład, gdzie d0jest rejestr, do którego chcesz przypisać. Starsze asemblery mają tylko jeden argument na instrukcję. W 6502 LDA #255(Load Accumulator) można argumentować, że Ajest on po lewej stronie, ale jest także po lewej stronie w STA wherever(Store Accumulator).
Steve314,
2
I nawet Intel 4004 (4-bitowy przodek rodziny 8086, z 8008 i 8080 pomiędzy nimi) został opracowany po przypisaniu w językach wysokiego poziomu. Jeśli zakładasz, że seria 8086 reprezentuje to, co zrobili asemblery w latach 50. i wcześniejszych, bardzo wątpię, że to prawda.
Steve314,
2

Na co warto, większość wypowiedzi w języku COBOL czytane od lewej do prawej, a więc dwa argumenty zostały nazwane pierwszy i ostatni przeznaczenia, takich jak: multiply salary by rate giving tax.

Nie będę jednak sugerować, że twój uczeń może preferować COBOL, z obawy, że (całkiem słusznie) zostanę oznaczony za wypowiedzenie tak niskiego, nieokrzesanego, bez smaku komentarza! :-)

Jerry Coffin
źródło
1

powiedziała, że ​​od lewej do prawej wydaje jej się bardziej naturalna, ponieważ tak większość z nas czyta naturalne języki.

Myślę, że to pomyłka. Z jednej strony możesz powiedzieć „przypisz 10 do x” lub „przenieś 10 do x”. Z drugiej strony możesz powiedzieć „ustaw x na 10” lub „x zamieni się w 10”.

Innymi słowy, w zależności od wyboru czasownika, zmienna przypisana do może być lub nie być przedmiotem i może, ale nie musi być po lewej stronie. Tak więc „to, co naturalne”, zależy wyłącznie od twojego zwyczajowego wyboru sformułowań reprezentujących zadanie.

Steve314
źródło
0

W pseudokodzie operator przypisania jest bardzo często zapisywany po prawej stronie. Na przykład

2*sqrt(x)/(3+y) -> z

W kalkulatorach Casio, nawet w wariantach nieprogramowalnych, zmienna przypisania jest również wyświetlana po prawej stronie

A+2B → C

W Forth zmienna również znajduje się po prawej stronie

expression variable !

W wersji x86 składnia Intela ma miejsce docelowe po lewej stronie, ale składnia GAS odwraca kolejność, wprowadzając zamieszanie dla wielu osób, szczególnie w przypadku instrukcji dotyczących kolejności parametrów, takich jak odejmowanie lub porównania. Te instrukcje są takie same dla 2 różnych dialektów

mov rax, rbx    ; Intel syntax
movq %rbx, %rax ; GAS syntax

Oba przenoszą wartość w rbx do rax. Nie znam innych języków asemblera po prawej stronie, takich jak GAS.

Niektóre platformy umieszczają wyrażenie po lewej stronie, a zmienną po prawej:

MOVE expression TO variable      COBOL
expression → variable            TI-BASIC, Casio BASIC
expression -> variable           BETA, R
put expression into variable     LiveCode

https://en.wikipedia.org/wiki/Assignment_%28computer_science%29#Notation

Większość języków przypisuje wartość po lewej stronie, co jest jednym z powodów łatwego wyrównywania operatorów, łatwiejszego do odczytania i rozpoznania zmiennej, ponieważ operatory przypisania i pozycje zmiennych nie będą się bardzo różnić w wierszach i łatwiej jest je odczytać jako „Niech zmienna będzie jakąś wartością”.

Jednak niektórzy wolą powiedzieć „przenieś wartość x na y” i napisz zmienną po prawej stronie.

phuclv
źródło
-1

Myślę, że to logiczne myślenie.
Najpierw musi być pole (zmienne), a następnie umieść w nim obiekt (wartość).
Nie umieszczasz obiektu w powietrzu, a następnie umieszczasz wokół niego pudełko.

Petruza
źródło
2
tak robisz w większości języków prawy bok jest oceniany przed lewym.
Javier
3
Jest to rodzaj „jazdy po prawej stronie drogi”. Wydaje się to logiczne, ale tylko dlatego, że zawsze tak było. Wszystkie wyższe kraje jadą po lewej stronie.
James Anderson
1
@JamesAnderson lepsze kraje? : o
nawfal
Masz rację, jest to logiczne tylko dla systemu pisania od lewej do prawej jako alfabetu łacińskiego, który, jak sądzę, jest używany w prawie każdym języku programowania, jeśli nie we wszystkich.
Petruza