Почему оператор присваивания присваивается левой стороне?

47

Я начала преподавать программирование подруге совсем недавно (мы используем Python), и когда мы начали обсуждать создание переменных и оператор присваивания, она спросила, почему значение справа присваивается имени слева, а не наоборот ,

Я не слишком много думал об этом раньше, потому что это казалось мне естественным, но она сказала, что слева направо кажется более естественным для нее, поскольку именно так большинство из нас читает естественные языки.

Я подумал об этом и пришел к выводу, что он значительно облегчает чтение кода, поскольку назначенные ему имена (которые программисту потребуется повторно использовать) легко видны и выровнены по левому краю.

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

В отличие от:

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

# What were the names again...?

Теперь мне интересно, есть ли другие причины для этого стандарта. Есть ли за этим история? Или есть какая-то техническая причина, почему это хороший вариант (я не знаю много о компиляторах)? И есть ли языки программирования, которые назначают на правую сторону?

voithos
источник
15
R можно назначить на правую сторону ( value -> variable).
Вы
Возможно, это связано с неправильными именами переменных? Как насчет 'numAligned = 2` и' chickensLeft = 2 + 5 '?
Работа
18
Имя твоего друга ... Йода?
Адриан
1
Если она начнет рассказывать об этом, мне интересно, что она будет делать, когда увидит C ++ & co.
BlackBear
3
Небольшое примечание: у Khan Academy есть несколько уроков по Python для начинающих программистов: khanacademy.org/#computer-science, а у Google есть несколько для более продвинутых: code.google.com/edu/languages/google-python-class/set- up.html
Линдон Фокс

Ответы:

56

То же самое @paxdiablo. Ранние языки программирования были написаны математиками - фактически, все они были. В математике, по ее собственному принципу - чтение слева направо - это имеет смысл в том, как это работает.

х = 2у - 4.

В математике вы бы сказали так: пусть х будет равен 2y -4.

Кроме того, даже в алгебре вы делаете это. Когда вы решаете уравнение для переменной, вы изолируете переменную, для которой вы решаете, слева. то есть y = mx + b;

Кроме того, как только целое семейство языков, например семейство C, имеет определенный синтаксис, его изменение обходится дороже.

Джонатан Хенсон
источник
19
@FarmBoy: в математике присваивание и равенство - это одно и то же, поскольку в формуле нет последовательности, как в компьютерах. (a равно b и в то же время b равно a)
Petruza
1
@FB за исключением некоторых функциональных языков с одним назначением, таких как, например, Erlang. Назначение и обеспечение равенства такие же, как в математике
Пер Стритцингер
18
@Petruza Нет, в математике присваивание и равенство не одно и то же. Если я говорю «Пусть x = 2y - 3», это отличается от «Таким образом, x = 2y - 3». Я математика, как правило, контекст различает их. Так как комментарий, оспаривающий меня, получил всеобщее признание, я упомяну, что у меня есть докторская степень. в математике я почти уверен в этом.
Эрик Уилсон
2
Я не знаю математики нигде рядом с доктором наук, я утверждаю, что, поскольку нет последовательности, в математике нет порядка выполнения ни в задании, ни в равенстве, в отличие от программирования, в котором обе стороны задания могут быть отличаются в определенный момент времени, и в конечном итоге они становятся равными в другой момент времени. Но в математике в задании, подобном тому, let a be...как нет времени, обе стороны задания также равны, поэтому задание фактически является равенством, не удивительно, почему оба используют один и тот же знак:=
Петруза
5
@Petruzza - но это секвенциальность. Математические документы пишутся от начала до конца так же, как и любой другой документ. Если я утверждаю x = 1в первой главе, но утверждаю x = 2во второй главе, это не какое-то ужасное противоречие - каждое утверждение применяется только в определенном контексте. Разница в императивном программировании отчасти заключается в устранении барьера (нам не нужно менять контекст), а отчасти в реализации и полезности.
Steve314
26

BASICОдин из первых компьютерных языков имел «правильную» форму:

10 LET AREA = HEIGHT * WIDTH

который соответствует математическому мышлению при указании переменной, например «Пусть H будет высотой объекта».

COBOLбыло также похоже на его COMPUTEзаявление. Как и в случае со многими другими способами, это могло быть просто произвольное решение, которое было перенесено на многие языки.


источник
7
Я полагаю, что эта форма более естественна, когда строки программы рассматриваются как «операторы», а не как «операции». Как I declare that X must equal THISи вместо, вместо более линейногоEvaluate THIS and store it in X
voithos
Подождите, бейсик был «ранним» компьютерным языком?
Алекс Фейнман
2
@ Алекс Учитывая, что это звучит с 1960-х годов, я бы сказал, что это довольно рано.
Бен Ричардс
1
С другой стороны, в COBOL вы можете написать MULTIPLY HEIGHT BY WIDTH GIVING AREA, поэтому переменная, которая получает результат, находится в самой правой части оператора.
user281377
25

На самом деле, есть язык программирования, который присваивается правой стороне: TI-BASIC ! Не только это, но он также не использует '=' в качестве оператора присваивания, а скорее использует стрелку, известную как оператор "STO".

Примеры:

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

В приведенном выше примере объявляются три переменные и им присваиваются значения. A будет 5, B будет 8, а C будет -3. Первое объявление / присвоение можно прочитать как «сохранить 5 как A».

Что касается того, почему TI-BASIC использует такую ​​систему для назначения, я объясняю это тем, что это язык программирования для калькулятора. Оператор «STO» в калькуляторах TI чаще всего использовался в обычных операциях калькулятора после вычисления числа. Если бы это был номер, который пользователь хотел запомнить, он нажал бы кнопку «STO», и caclulator запросит у них имя (автоматически задействует альфа-блокировку, чтобы нажатия клавиш производили буквы вместо цифр):

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

и пользователь мог бы назвать переменную как угодно. Необходимость включить альфа-блокировку, ввести имя, затем нажать «STO», и нажатие клавиши «Ans» было бы слишком громоздким для обычных операций. Поскольку все функции калькулятора доступны в TI-BASIC, никакие другие операторы присваивания не были добавлены, поскольку «STO» выполнял ту же задачу, хотя и в обратном направлении по сравнению с большинством других языков.

(Анекдот: TI-BASIC был одним из первых языков, которые я выучил, поэтому, когда я впервые изучал Java в колледже, я чувствовал, что назначение на ЛЕВОЙ было необычным и «задом наперед»!)

diceguyd30
источник
+1 Я полностью забыл это! TI Basic тоже был моим первым языком, но я не помню эту деталь.
Барджак
На самом деле оператор STO ближе к тому, как работает машина и как на самом деле работает любой язык. Значение сначала вычисляется, а затем сохраняется в памяти.
Крат
15

Эвристика 1: Когда вы сталкиваетесь с несколькими возможными способами сделать что-то во время разработки языка, выберите наиболее распространенный, наиболее интуитивный, иначе вы получите Perl +.

Теперь, как это более естественно (по крайней мере, для говорящего по-английски)? Давайте посмотрим, как мы пишем / говорим по-английски:

Стивену сейчас 10 лет (в отличие от 10 лет Стивену сейчас). Я вешу более 190 фунтов (в отличие от более 190 фунтов, которые я вешу).

В коде:

steven = 10
i > 190

Следующее также звучит более естественно:

«Если Мэри 18 лет, она может иметь конфету». «Если я моложе 21 года, то я попрошу у моего брата текилу».

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

чем:

«Если 18 лет, то Мария…» «Если 21 больше моего возраста ...»

Теперь код:

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

Обратите внимание, что это не естественно ни для программистов, ни для носителей английского языка. Предложения звучат как йода-говорящий, а код по прозвищу йода-условия. Это может быть полезно в C ++, но я уверен, что большинство людей с этим согласятся: если бы компилятор мог сделать тяжелую работу и облегчить потребность в yoda-условиях, жизнь была бы немного проще.

Конечно, можно привыкнуть ко всему. Например, число 81 записывается как:

Восемьдесят один (английский) Восемьдесят один и один (испанский) Один и восемьдесят (немецкий).

Наконец, есть 4! 24 правильных способа сказать «зеленое яблоко лежит на столе» на русском языке - порядок (почти) не имеет значения, за исключением того, что «on» должно сочетаться с «table». Так что, если вы являетесь носителем русского языка (например), то вам может быть все равно, пишете ли вы один a = 10или 10 = aпотому, что оба кажутся одинаково естественными.

В то время как лингвистика - захватывающий предмет, я никогда официально не изучал это и не знаю так много языков. Надеюсь, я предоставил достаточно контрпримеров.

работа
источник
4
... а по-французски 81 называется "четыре раза двадцать один" ... :)
Мартин Сойка,
1
@ Мартин, я думаю, что датский очень похож.
CVN
7
@Martin Это действительно странно, потому что четыре раза двадцать один - 84.
Питер Олсон
6
@Peter: у вас неверные скобки, это(four times twenty) one
SingleNegationElimination
4
@Job: Читая твое "Если 18 лет, Мэри ...", мне неизбежно напомнили, что Йода говорит: "Когда ты достигнешь девятисот лет, выглядишь так хорошо, как не будешь, а?" Возвращение джедая :-)
Йорики
11

Это началось с Фортрана в 1950-х годах. Где FORTRAN был аббревиатурой от FORmula TRANslation - речь идет о простых алгебраических уравнениях, которые по соглашению всегда назначаются слева.

С другой стороны, его почти современный COBOL должен был быть похожим на английский и иметь право (в основном!).

MOVE 1 TO COUNTER.
ADD +1 TO LINE-CNT.
MULTIPLY QTY BY PRICE GIVING ITEM-PRICE.
Джеймс Андерсон
источник
Я думаю, что это лучший пример здесь, потому что он прекрасно иллюстрирует контекст на языке, где он может быть прочитан обычным носителем английского языка, но имеет правильное назначение. Я говорю, что это важно, потому что большое количество людей, говорящих по-английски, будут читать только слева направо, чтобы это задание имело смысл, что абсолютно не соответствует действительности.
Джошуа Хеджес
5

Ну, как указал @ diceguyd30, есть обе записи.

  • <Identifier> = <Value>означает «пусть идентификатор будет значением ». Или расширить это: Определить (или переопределить) переменную Identifier to Value .
  • <Value> -> <Identifier>означает «сохранить значение для идентификатора ». Или расширить это: Поместите Значение в место, обозначенное Идентификатором .

Конечно, вообще говоря, Идентификатор может фактически быть любым L-значением.

Первый подход учитывает абстрактную концепцию переменных, а второй - больше о фактическом хранении.

Обратите внимание, что первый подход также распространен в языках, которые не имеют назначений. Также обратите внимание, что определение и назначение переменной относительно близки по <Type> <Identifier> = <Value>сравнению с <Identifier> = <Value>.

back2dos
источник
3

Как уже упоминалось, все ранние компьютерные языки работали очень хорошо. Например, Фортран, появившийся за много лет до Бейсика.

На самом деле имеет смысл иметь присвоенную переменную слева от выражения присваивания. В некоторых языках у вас может быть несколько разных перегруженных подпрограмм с тем же именем, возвращающих разные типы результатов. Позволяя компилятору сначала увидеть тип назначенной переменной, он знает, какую перегруженную подпрограмму вызывать или какую неявную приведение генерировать при преобразовании из (например) целого числа в число с плавающей точкой. Это немного упрощенное объяснение, но, надеюсь, вы поняли идею.

Дейв Джуэлл
источник
2
Я понимаю ваше объяснение, но не мог ли компилятор просто заглянуть в будущее до конца утверждения?
Войтос
Это может упростить лексер в современных языках, но сколько языков программирования даже поддерживают именованные методы, не говоря уже о перегрузках методов, когда этот вид синтаксиса был новым?
CVN
2
Привет @voithos. Да - компилятор мог бы смотреть в будущее, но это, вероятно, было бы неприемлемым уровнем сложности в первые дни написания компилятора, который часто был написан вручную на ассемблере! Я думаю, что размещение назначенной переменной слева - это прагматичный выбор: и человеку, и машине легче анализировать.
Дейв Джуэлл
Я думаю, что это было бы тривиально для назначения назначить на право. Когда выражение типа 3 + 4 == 6 + 7, обе стороны вычисляются перед оператором, потому что язык определяется рекурсивно. Элемент языка 'variable = expression' может быть легко изменен на 'expression = variable'. Является ли это причиной неоднозначных ситуаций, зависит от остальной части языка.
Крат
@Kratz - это, безусловно, верно для компиляторов, но, возможно, была небольшая проблема для очень старых интерпретируемых языков, которые работали с токенизированным источником. OTOH, который мог бы предпочесть переменную справа, а не переменную слева.
Steve314
3

Это может быть остатком ранних алгоритмов разбора. Помните, что разбор LR был изобретен только в 1965 году, и вполне может быть, что у парсеров LL были проблемы (в пределах временных и пространственных ограничений машин в то время), которые шли наоборот. Рассмотреть возможность:

identifier = function();
function();

Два явно устранены неоднозначностью от второго жетона. С другой стороны,

function() = identifier;
function();

Не весело. Это ухудшается, когда вы начинаете вкладывать выражения присваивания.

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

Конечно, более легкое устранение неоднозначности для машин также означает более простое устранение неоднозначности для людей. Другой простой пример - поиск инициализации любого заданного идентификатора.

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

Легко, просто посмотрите на левую сторону. Правая сторона, с другой стороны

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

Особенно, когда вы не можете grepперфорировать карты, гораздо сложнее найти нужный идентификатор.

DeadMG
источник
Именно то, о чем я думал, да.
Войтос
2

Языки ассемблера имеют место назначения как часть левого кода операции. Языки более высокого уровня, как правило, следовали соглашениям языков предшественников.

Когда вы видите =(или :=для диалектов Pascalish), вы можете произносить их как is assigned the value, тогда смысл слева направо будет иметь смысл (потому что мы также читаем слева направо на большинстве языков). Поскольку языки программирования преимущественно разрабатывались людьми, читающими слева направо, соглашения придерживались.

Это тип зависимости от пути . Я полагаю, что если бы компьютерное программирование было изобретено людьми, которые говорили на иврите или арабском (или каком-либо другом языке справа налево), то я подозреваю, что мы бы указали пункт назначения справа.

Tangurena
источник
Да, но я подозреваю, что текст в редакторах также будет выровнен по правому краю ...
voithos
8
Вы не можете так обобщать о языках ассемблера. Они различаются относительно того, где находится операнд назначения.
fast_now
2
@quickly_now: верно; на самом деле, большинство примитивных машинных языков (даже по сегодняшним меркам даже не ассемблеры) даже не имели пункта назначения, поскольку обычно там был только один или два аккумулятора общего назначения. большинство операций подразумевали аккумулятор в качестве пункта назначения, за исключением кодов операций «store», в которых указывался только адрес памяти, а не источник (который был аккумулятором). Я действительно не думаю, что это оказало какое-либо влияние на синтаксис присваивания для языков, подобных Алголу.
Хавьер
3
@Tangurena - Некоторые языки ассемблера имеют описание слева. Не из кода операции (это собранный объектный код), а слева от списка аргументов для мнемоники инструкции. Тем не менее, другие имеют пункт назначения справа. В ассемблере 68000 вы mov.b #255, d0, например, напишите , где d0находится регистр для назначения. Старые ассемблеры имеют только один аргумент на инструкцию. В 6502 LDA #255(Load Accumulator) вы можете утверждать, что Aслева, но также и в STA wherever(Store Accumulator).
Steve314
2
И даже Intel 4004 (4-битный предок семейства 8086, между 8008 и 8080 между ними) был разработан после назначения на языках высокого уровня. Если вы предполагаете, что серия 8086 является репрезентативной для того, что ассемблеры делали в 50-х и ранее, я очень сомневаюсь, что это правда.
Steve314
2

Для чего это стоит, большинство операторов , в COBOL читать слева направо, так что оба операнда были названы первый и последний пункт назначения, как: multiply salary by rate giving tax.

Я не буду, однако, предполагать, что ваш ученик может предпочесть КОБОЛ, потому что боится, что меня (совершенно справедливо) пометят за такой низкий, неуклюжий, безвкусный комментарий! :-)

Джерри Гроб
источник
1

она сказала, что слева направо кажется более естественным для нее, поскольку именно так большинство из нас читает естественные языки.

Я думаю, что это ошибка. С одной стороны, вы можете сказать «назначить 10 на х» или «переместить 10 на х». С другой стороны, вы можете сказать «установить х в 10» или «х становится 10».

Другими словами, в зависимости от вашего выбора глагола, назначаемая переменная может быть или не быть субъектом, а может и не быть слева. Таким образом, «то, что естественно», полностью зависит от вашего привычного выбора формулировки для представления задания.

Steve314
источник
0

В псевдокоде оператор присваивания очень часто пишется справа. Например

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

В калькуляторах Casio, даже непрограммируемых вариантах, переменная присваивания также отображается справа

A+2B → C

В Forth переменная тоже справа

expression variable !

В x86 синтаксис Intel имеет место назначения слева, но синтаксис GAS меняет порядок в обратном порядке, что вводит в заблуждение многих людей, особенно в отношении инструкций, касающихся порядка параметров, таких как вычитание или сравнение. Эти инструкции одинаковы на 2 разных диалектах

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

Они оба перемещают значение в rbx в rax. Не знаю других языков ассемблера, напишите пункт назначения справа, как ГАЗ.

Некоторые платформы помещают выражение слева и переменную справа:

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

Большинство языков присваивают значение слева, что является одной из причин, по которой легко выравнивать операторы, легче читать и распознавать переменную, поскольку операторы присваивания и положения переменных не будут сильно различаться в строках, и их легче читать как msgstr "пусть переменная будет некоторым значением".

Однако некоторые люди предпочитают говорить «переместить значение x в y» и написать переменную справа.

phuclv
источник
-1

Я думаю, что это следует логическому мышлению.
Сначала должен быть блок (переменная), затем вы помещаете в него объект (значение).
Вы не помещаете объект в воздух и затем помещаете коробку вокруг этого.

Petruza
источник
2
да, вы делаете в большинстве языков правая сторона оценивается перед левой.
Хавьер
3
Это тип «Вождение по правой стороне дороги». Это кажется логичным, но только потому, что так, как вы всегда это делали. Все превосходящие страны едут налево.
Джеймс Андерсон
1
@JamesAnderson лучшие страны? : o
nawfal
Вы правы, это логично только для системы письма слева направо в качестве римского алфавита, который, я думаю, используется почти всеми языками программирования, если не всеми.
Петруза