Почему "=" означает присваивание?

https://www.hillelwayne.com/post/equals-as-assignment/
  • Перевод
Давайте посмотрим на следующий код:

a = 1
a = a + 1
print(a)

В среде ФП часто критикуют данный момент императивного программирования: «Как так может быть, что a = a + 1? Это всё равно что сказать „1 = 2“. В мутабельном присваивании нет смысла».

Здесь мы наблюдаем несовпадение обозначения: «равно» должно обозначать «равенство», когда на практике оно обозначает «присвоить». Я согласен с этой критикой и считаю, что это неудачная нотация. Но также мне известно, что в некоторых языках вместо a = a + 1 пишут выражение a := a + 1. Почему же эта запись не является нормой?

На этот вопрос обычно отвечают «потому что так сделано в C». Но это похоже на перекладывание ответственности на кого-то другого: кто из нас знает, почему так сделано в C? Давайте разбираться вместе!

Большая четвёрка


В начале 1960-ых существовало четыре доминирующих высокоуровневых языка: COBOL, FORTRAN II, ALGOL-60, и LISP. В то время, программисты разбивали присваивание на два класса: инициализацию (initialization) — когда вы впервые определяете переменную, и переприсвоение (reassignment) — когда вы вы изменяется значение существующей переменной.

Итак, давайте добавим комментарии к нашему примеру на Python и получим следующий код:

a = 1 # Инициализация
a = a + 1 # Переприсвоение
print(a)

В то время люди не пользовались конкретно этими терминами для обозначения операций, но по сути это было как раз то, что делал каждый программист. В таблице ниже вы можете увидеть, какие из операторов использовались для каждого языка, и как выполнялась проверка на равенство.
Язык Инициализация Присваивание Равенство
FORTRAN = = .EQ.
COBOL INITIALIZE MOVE [1] EQUAL
ALGOL N/A := =
LISP let set equal

В ALGOL не было отдельного оператора для инициализации — вместо этого вы создавали переменную определенного типа и затем использовали оператор для присвоения ей чего-либо. Вы могли написать integer x; x := 5;, но не x := 5;. Единственный язык из списка, который использовал = для присваивания, это FORTRAN — и он выглядит подходящим кандидатом для ответа на наш вопрос.

Но мы-то с вами знаем, что C происходит от ALGOL; что, в свою очередь, означает, что по какой-то причине было решено отказаться от оператора присваивания := и изменить значение оператора = с проверки на равенство…

ALGOL порождает CPL


ALGOL-60, скорее всего, является одним из самых влиятельных языков программирования в истории computer science. Вероятно, что при всём этом он также является одним из самых бесполезных языков. В основной спецификации языка намеренно не было предусмотрено никакой функциональности для ввода/вывода. Вы могли «захардкодить» вводы и измерять выводы, но если вам нужно было сделать с ними что-либо полезное, вам требовалось найти компилятор, который расширял бы базовый язык. ALGOL был спроектирован с целью исследования алгоритмов и поэтому он «ломался», когда вы пытались сделать на нём что-либо ещё.

Однако, он оказался настолько «крепким» языком, что другие захотели обобщить его для использования в бизнесе и в промышленности. Первую подобную попытку предприняли Кристофер Страчи и Кембриджский университет. Получившийся в итоге язык CPL добавил к функциональности ALGOL достаточное количество инновационных возможностей, о большей части которых мы в дальнейшем глубоко пожалели. Одной из них было определение с инициализацией, в котором переменная могла быть инициализирована и присвоена в одном выражении. Теперь вместо того, чтобы писать x; x := 5; вы могли просто написать integer x = 5. Просто супер!

Но здесь мы переключились с := на =. Это происходит потому, что в CPL было три типа инициализации переменной:

  • = означало инициализацию по значению.
  • ≃ означала инициализацию по ссылке, поэтому если x ≃ y, то переприсваивание x также изменяет y. Но если вы написали x ≃ y + 1 и попробовали переприсвоить x, то программа бы «упала».
  • ≡ означает инициализацию через подстановку, т.е. превращение x в функцию, не принимающую аргументов (niladic function), которая вычисляет правостороннее значение каждый раз, когда её используют. При этом нигде не объясняется, что должно случиться, если вы попробуете переприсвоить x — и я, поверьте, тоже не слишком хочу знать это.

Проблема: теперь = использовался и для инициализации, и для равенства. К счастью, на практике в CPL эти варианты использования символа были четко разграничены: если вы где-либо писали =, то было однозначно понятно, что имелось в виду.

Всего год спустя Кен Айверсон создаст APL, который станет использовать символ для всех видов присваиваний. Поскольку на большинстве клавиатур такой клавиши нет и никогда не было, от него быстро откажется и сам автор — его следующий язык, J, тоже будет использовать для присваиваний символ =:[2]. Однако, APL глубоко повлиял на S, который в свою очередь глубоко повлиял на R — вот почему <- является предпочтительным оператором присваивания в R.

CPL порождает BCPL


CPL был замечательным языком, обладавшим всего одним небольшим недостатком: ни у кого не получалось написать его реализацию. Несколько человек смогли частично реализовать различные подмножества из его «фич», но этот язык оказался слишком большим и сложным для компиляторов той эпохи. Поэтому неудивительно, что Мартин Ричардс решил избавиться от ненужной сложности ящика и создал BCPL. Первый компилятор BCPL появился в 1967 году… а первый компилятор CPL — лишь в 1970-м.

Среди многих других упрощений оказались и правила «трёх типов инициализации», которые приказали долго жить. Ричардс считал, что выражения-подстановки были вещью узкоспециальной, и их можно было заменить функциями (то же самое, по его мнению, касалось и присваиваний). Поэтому он совместил их всех в простое =, за исключением наименований адресов глобальной памяти, которые использовали :. Как и в случае CPL, = представляло собой проверку на равенство. Для присвоения (reassignment), он использовал := — аналогично тому, как это сделали CPL и ALGOL. Многие из последовавших после языков также следовали этому соглашению: = для инициализации, := для присваивания, = для равенства. Но в широкие массы это пошло тогда, когда Никлаус Вирт создал Pascal — вот почему сегодня мы называем подобные обозначения «в стиле Pascal».

Насколько мне известно, BCPL был также первым «слабо типизированным» языком, поскольку единственным типом данных было машинное слово (data word)[3]. Это позволило сделать компилятор куда более портабельным за счет потенциального увеличения количества логических ошибок, но Ричардс надеялся на то, что улучшения в процессе и наименования с описанием позволят противостоять этому. Помимо все этого, именно в BCPL впервые появились фигурные скобки с целью определения блоков.

BCPL порождает B


Кен Томпсон хотел, чтобы BCPL мог выполняться на PDP-7. Несмотря на то, что у BCPL был «компактный компилятор», он всё ещё был в четыре раза больше, чем минимальный объем рабочей памяти на PDP-7 (16 кБ вместо 4 кБ). Поэтому Томпсону требовалось создать новый, более минималистичный язык. Также по личным эстетическим причинам он хотел минимизировать количество символов в исходном коде. Это и повлияло на дизайн языка B сильнее всего; вот почему в нём появились такие операторы, как ++ и --.

Если вы оставите в стороне использование поименованных адресов глобальной памяти, в BCPL всегда использовались следующие обозначения: = для инициализации и := для переприсваивания (reassignment). Томпсон решил, что эти вещи можно совместить в единый токен, который можно использовать для всех видов присваивания, и выбрал =, поскольку оно было короче. Однако, это привнесло некоторую неоднозначность: если x уже был объявлен, то чем было x = y — присваиванием или проверкой на равенство? И это ещё не всё — в некоторых случаях предполагалось, что это обе операции сразу! Поэтому он был вынужден добавить новый токен == как единую форму выражения смысла «равняется этому». Как выражался сам Томпсон:
Поскольку присваивание в типовой программе встречается примерно в два раза чаще, чем сравнение на равенство, уместно было сделать оператор присваивания вполовину короче.
За время, прошедшее между появлением BCPL и B, была создана Simula 67, первый объектно-ориентированный язык. Simula последовала соглашениям ALGOL о строгом разделении шагов инициализации и переприсвоения. Алан Кей примерно в это же время начал работу над Smalltalk, который добавил блоки, но последовал такому же синтаксису.

Томпсон (к которому присоединился Денис Ритчи) выпустил первую версию B примерно в 1969 году. Так что вплоть до 1971 года (примерно) большинство новых языков использовали для присваивания обозначение :=.

B порождает C


… остальное – уже история.

Хорошо, есть ещё кое-что, о чём стоит рассказать. ML вышел год спустя, и, насколько мне известно, был первым языком, который привлек серьезное внимание к чистым функциям и отсутствию мутаций. Но в нем по-прежнему был спасательный круг в виде ссылочных ячеек (reference cells), которые можно было переприсваивать новым значениям при помощи оператора :=.

Начиная с 1980, мы наблюдаем рост популярности новых императивных языков, ориентированных на корректность — в частности, Eiffel и Ada, оба из которых используют для операции присваивания символ :=.

Если посмотреть на всю картину в целом, = никогда не был «естественным выбором» для оператора присваивания. Почти все языки в семейном дереве ALGOL использовали вместо этого для присваивания :=, возможно в силу того, что = было столь тесно ассоциировано с равенством. В наши дни большинство языков использует = поскольку его использует C, и мы можем проследить эту историю до CPL, который представлял собой тот ещё бардак.

Примечания


1. В этом месте COBOL становится очень странным. У них есть несколько операторов, которые могут неявно мутировать, вроде ADD TO и COMPUTE. COBOL — плохой язык.
2. Мне нравится думать, что это было своеобразным приколом над :=, хотя на самом деле этот оператор согласован с остальными частями языка, который использует . и : как суффиксы глаголов.
3. Позже в BCPL добавят ключевое слово для типа с плавающей запятой. И когда я говорю «позже», я имею в виду 2018 год.
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 205
  • +19
    В переводе упущена отличнейшая сноска:
    «BCPL later adding a floating point keyword. And when I say “later”, I mean in 2018.»
    • +3
      Просто отлично. Спасибо, добавил!
    • +11
      Напомнило историю про современные технические достижения и ширину крупа лошади.
      • 0
        Для тех, кто, как и я, не слышал эту историю раньше, приведу ее здесь.
        Сама история
        По бокам космического корабля «Кеннеди» размещаются два двигателя по 5 футов шириной. Конструкторы корабля хотели бы сделать эти двигатели еще шире, но не смогли. Почему? Дело в том, что двигатели эти доставлялись по железной дороге, которая проходит по узкому туннелю. Расстояние между рельсами стандартное: 4 фута 8.5 дюйма, поэтому конструкторы могли сделать двигатели только шириной 5 футов. Возникает вопрос: почему расстояние между рельсами 4 фута 8.5 дюйма? Откуда взялась эта цифра? Оказывается, что железную дорогу в Штатах делали такую же, как и в Англии, а в Англии делали железнодорожные вагоны по тому же принципу, что и трамвайные, а первые трамваи производились в Англии по образу и подобию конки. А длина оси конки составляла как раз 4 фута 8.5 дюйма! Но почему? Потому что конки делали с тем расчетом, чтобы их оси попадали в колеи на английских дорогах, чтобы колеса меньше изнашивались, а расстояние между колеями в Англии как раз 4 фута 8.5 дюйма! Отчего так? Да просто дороги в Великобритании стали делать римляне, подводя их под размер своих боевых колесниц, и длина оси стандартной римской колесницы равнялась… правильно, 4 футам 8.5 дюймам! Ну вот теперь мы докопались, откуда взялся этот размер, но все же почему римлянам вздумалось делать свои колесницы с осями именно такой длины? А вот почему: в такую колесницу запрягали обычно двух лошадей. А 4 фута 8.5 дюйма — это был как раз размер двух лошадиных задниц! Делать ось колесницы длиннее было неудобно, так как это нарушало бы равновесие колесницы. Следовательно, вот и ответ на самый первый вопрос: даже теперь, когда человек вышел в космос, его наивысшие технические достижения напрямую зависят от РАЗМЕРА ЛОШАДИНОЙ ЗАДНИЦЫ.
        • 0
          Эту историю уже развенчивали в разных местах, по пунктам.

          Начиная с банального фактического несоответствия — космического корабля «Кеннеди» никогда не существовало, такое название носит космодром.

          Прочие аргументы:

          — ж/д габарит несколько шире ж/д колеи. Для «обычной» в два раза, для узкоколейки — аж в три, и в общем случае линейной зависимости нет.
          — диаметр боковых ускорителей шаттла 3,7 метра, что равняется чуть более 12 футов, и в габарит вообще не вписывается:
          «However, the diameter of the shuttle boosters (12 feet) exceeds the width of all loading gauge standards in the USA (which differ mostly in height, the width is generally 10 feet 8 inches).»
          space.stackexchange.com/questions/4064/how-fair-is-the-claim-that-shuttle-boosters-diameter-is-dictated-by-the-railroad

          Негабаритные грузы на мыс Каннаверал доставляются морским путём — так было со ступенями «Сатурна-V» диаметром 10,1 м и внешними топливными баки шаттлов восьмиметрового диаметра. Это больше десятка лошадиных задниц.

          — если Англия шла по следам римских стандартов, почему это не сделали другие европейские страны? В Ирландии, например, существовало аж 6 стандартов колеи, да и в самой Англии встречались и 1600 мм, и даже 2135 мм то есть 7 футов (Большая Западная дорога ). В США тоже был полный раздрай, благо вместо единой сети каждая жд корпорация строила собственную.

          — Стефенсоновская колея (эти самые 1435 мм) устаканилась как основная по двум прозаичным причинам — из-за компоновки паровоза Стефенсона, а также из-за удобного размера шпал в качестве которых использовались стандартные горные крепи.

          — Расстояние между крупами лошадей зависит исключительно от типа и вида упряжи (дышловая и оглобельная) и количества впряжённых попарно лошадей (от 1,2 до 1,8 м). А ведь ещё и тройки лошадей запрягали...)))

          И римская армия не использовала колесницы (не очень подходящий транспорт для тех ТВД, где оперировали легионы), у них и конница-то была или союзническая, или наемники.
          For starters, the Roman army did not use chariots for warfare. Chariots were technologically obsolete by 600 BCE, centuries before the rise of Rome.…
          The other aspect of this standardization urban legend that is pure fiction is the suggestion that the standard track gauge in the U.S. has always been 4 feet 8-1/2 inches.
          At the beginning of the American Civil War in 1861, there were more than 20 different railroad track gauges in the U.S. ranging from 3 feet to 6 feet.…
          Everyone seems to agree that this odd track size did originate in England from a railway pioneer named George Stephenson who used the 4 feet 8-1/2 inch track gauge when building the first public rail line, the Liverpool & Manchester Railway, in 1830.…
          How ever the 4 foot 8-1/2 inch track gauge happened, it’s clear that the Roman military specification for “Chariots, War, Two-Horse” had nothing to do with it.
          (это из выступления собственно NASA по поводу этой байки — https://standards.nasa.gov/documents/RomanChariots.pdf )

          еще подробности: krazzzer.livejournal.com/35884.html

          А вот российская космонавтика в большей степени ограничена железнодорожным габаритом: habr.com/post/211054
      • +5
        Самая логичная запись присваивания — в ранних версиях Рапиры:
        Х+Y -> Х
        она отражает тот факт, что сначала вычисляется выражение и уже потом результат присваивается переменной.
        • +8
          С одной стороны — да, но тогда будет сложнее искать инициализированные переменные в коде, ведь конец строки везде разный. Куда проще пробежаться глазами по одной линии.
        • +2

          В R и сейчас так писать можно.

          • +1
            вот именно так, в постфиксной записи оператора присваивания?
            В R вроде ж присваивание префиксное:
            X <- X+Y
            • +2

              Да, именно в постфиксной. Можете посмотреть в документации: https://cran.r-project.org/doc/manuals/r-release/R-intro.html#Vectors-and-assignment.

              • 0
                а, ясно. Просто в базовых «учебниках для чайников» такого варианта не находится.
              • 0
                Да, в любую сторону, и иногда, например в конвейерных пайпах, логичнее и «красивее» записать результат справа (dt%>%filter()%>%group_by()%>%summarize()->res)
              • 0
                Но ради всего святого, никогда так не делайте!

                Те, кто пользуются dplyr, знают, что еще есть pipe операторы %<>%, которые избавляют от необходимости писать каждый раз что-то вроде df < — df %>%… По моему опыту, на практике их стоит избегать в кусках кода, которые часто могут меняться.
              • 0
                := из той же оперы, этот аналогично БНФ записи, т.е. это не присвоение как таковое, а указание, что под терминалом, который слева, понимается то, что справа.
                • 0

                  Куда проще, как по мне, использовать нотацию обычного отображения x -> w = x + 1

                • +2

                  если воспринимать "a" как букву/название то действительно может показаться что a=a+1 — странно, но если сместиться на парадигму что "a" это у нас грубо говоря именнованый акцесс к памяти то
                  a=1 //записать в область памяти с меткой а значение 1,
                  a=a+1 //записать в область памяти с меткой а увеличнное на 1 предыдущее значение…

                  • 0

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

                    • +7
                      Тут есть большой парадокс: вроде как все работали с переменными на уроках математики и вроде как должно быть проще именно такие переменные использовать (наиболее близки они к «школьно-математическим» в Prolog'е)… ан нет: на практике «сместиться на парадигму» и начать воспринимать переменную как «имя коробки с содержимым» людям легко, а вот как раз «функциональщина» — воспринимается с преогромным трудом.

                      Интересно — почему так…
                      • +1
                        На уроках математики все же в одной формуле одна переменная по разные стороны знака равенства (или неравенства) подразумевает одинаковое значение.

                        а, ниже разжевали лучше: habrahabr.ru/post/353292/#comment_10750626
                        • +1
                          Нифига там не разжевали. «Математические» переменные — это Prolog. Вот там как раз можно написать «x = 2y + 1» и это даст нам как возможность исходя из «x = 1» получить «y = 0» и, наоборот, из «y = 1» получить «x = 3».

                          Вот только язык этот считается эзотерическим и что-то там на нём посчитать — сложнее для большинства программистов, чем на большинстве других функциональных языках.

                          Вот как-то так получается, что чем дальше от «математики» и ближе к «железу», тем проще и понятней…
                          • 0
                            Пошаговое решение проблемы легче обозревается, потому что разбивается на малые, за раз обозримые части. А при функциональном/логическом подходе, напротив, надо всю запись целиком воспринимать одномоментно.
                        • 0
                          Интересно — почему так…

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


                          Ну вот простейший пример на haskell:


                          -- Example ha.1:
                          primes = map head $ iterate (\(x:xs) -> [y | y<-xs, y `mod` x /= 0 ]) [2..]

                          Или такой:


                          -- Example ha.2:
                          primes = 2 : [x | x <- [3, 5..], 
                            (
                              [
                                y | y <- [2..(div x 2)],
                                mod x y == 0
                              ] == []
                            )
                          ]

                          И сравните это по читабельности ну например с python:


                          # Example py.1:
                          (x for x in range(2,n) if x == 2 or (x & 1 and not any(y for y in range(2,x) if not x%y)))

                          Я не про эффективность тут, а про читабельность, но…
                          даже если что-то на сите ...


                          # Example py.2:
                          def primes():
                            '''Generator for primes via the sieve of eratosthenes'''
                            mSE = {}
                            x = 2
                            while 1:
                              if x not in mSE:
                                yield x
                                mSE[x*x] = [x]
                              else:
                                for y in mSE[x]:
                                  mSE.setdefault(y+x, []).append(y)
                                del mSE[x]
                              x += 1

                          Вторая причина: не все языки, на которых легко писать код, легко затем и читать.


                          Но зато "функциональщина" позволяет перекладывать многия математические формулы практически один в один, см. Функциональное программирование на языке Haskell, стр 109.


                          Ну и тут нас догоняет третья причина: как раз про эффективность — не многие представляют себе во что весь этот "сахар" и "соль" в результате развернутся, а хочется же понимать это сразу без утомительного переделывания/оптимизации потом (а мы помним иногда код плохо читается ;).


                          У традиционных императивных языков это, как правило, не так, ну или как минимум много очевиднее (и даже при наличии некоторой "магии").
                          Т.е. в примере py.1 выше сразу очевидно что критерий not any(y for y in range(2,x) if not x%y) абсолютно не эффективен (уже на относительно небольших числах).
                          По крайней мере пока python не научится оптимизировать суб-генераторы.

                          • 0
                            И сравните это по читабельности ну например с python:
                            И на то и на другое будут жаловаться. А вот заведите массивчик в BASIC и заполните его — и всё «станет ясно»…

                            Хотя, вроде как и Python и Haskel ближе к оригинальному математическому определению… и короче…

                            P.S. Тут как бы надо сказать что речь идёт о моём и, в то же время, не моём опыте. Об опыте помощи людям с программированием. Отсюда и такие странные выражения. Потому что вот мне лично вариант на Haskell кажется чуть ли не самым понятным и читаемым… но долгий опыт показал мне что я — скорее исключение, чем правило…

                            P.P.S. Не многие представляют себе во что весь этот «сахар» и «соль» в результате развернутся, а хочется же понимать это сразу без утомительного переделывания/оптимизации потом — это уже совсем другая история. Вопросы эффективности программы на Haskell — это отдельная песня, но для очень большого процента людей даже понять вот что вы там понаписали — проблема. Безотностительно к тому, во что оно потом транслируется…
                            • 0
                              … но долгий опыт показал мне что я — скорее исключение, чем правило…

                              Аналогично,… однако мне мой долгий опыт ещё подсказывает, что я видимо не в состоянии привить кому-либо что-нибудь функциональное (я тут не про азы).
                              Однако самостоятельно те же люди много позже смогли осилить ну например тот же haskell.
                              При том, что с императивными языками любой группы (включая скриптовые, типа питон) никогда особых проблем не наблюдалось (ну если человек в принципе обучаем программированию).

                          • 0
                            Интересно — почему так…

                            Да просто потому, что в математике это утверждение, информация, исходные данные для преобразований, а в программе — предписание для компьютера (разумеется, при посредничестве интерпретатора или компилятора)
                            • 0
                              И? Почему вдруг «предписание для компьютера» вам стало вдруг проще написать, чем простое описание «исходных данных для преобразований» — примерно как тут?
                              • 0
                                А когда я утверждал что-то подобное?
                                • 0
                                  Потому что шаг алгоритма легче объять разумом, чем формулу/абстракцию целиком.
                              • 0
                                1. в школьной математике нет «переменных»
                                2. есть разные «на практике» — я знаю кучу людей, которым как раз таки сложно понимать «коробочки». Потому что они математики, а не программисты.
                                3. я получал традиционное IT образование, и мне функциональщина гораздо более понятна и естественна, чем императивщина, то есть опять же, не стоит думать, что если в твоём окружении что-то происходит, то это происходит везде и со всеми.
                                • 0
                                  в школьной математике нет «переменных»

                                  А что такое x, y и т.п. в курсе дифференциального исчисления?
                                  В алгебраических доказательствах?
                                  • 0
                                    И вообще, слово «переменная» прямым текстом в учебниках есть.
                                    Например, М.Я.Выгодский. Справочник по элементарной математике — М., 1966 г., глава VI:
                                    параграф 1. «Постоянные и переменные величины»
                                    параграф 2. «Функциональная зависимость между двумя переменными»

                            • +4

                              Тоже думаю, что проблема преувеличена, и иммутабельность здесь не при чем.

                              • +2

                                Проблема есть на самом деле...


                                Я вот как-то поправил одну ошибку в коде калибрования некоторого устройства, измеряющего радиационный фон:


                                - if ((R = NominalR) || (Rq <= EstimatedMinEnvR)) {
                                + if ((R == NominalR) || (Rq <= EstimatedMinEnvR)) {
                                    R = (R + Rq)/2;
                                    return;
                                  }

                                Лишнее наверно говорить, что в той версии калибровка естественно не работала, от слова вовсе…
                                И это к сожалению не единственный случай на моей памяти.

                              • –19
                                Проблема высосана из мизинца левой ноги.
                                x = 2y + 1 в школе ни у кого не вызывало вопросов.
                                Не можешь запомнить нотацию языка, не программируй на нём.
                                • 0

                                  deleted

                                  • +20

                                    Проблема, может, и невелика, но ваш комментарий, как раз, свидетельствует о её существовании.


                                    x = 2y + 1 в школе ни у кого не вызывало вопросов.

                                    Именно потому, что это равенство, и оно не обладает описанным в статье противоречием. Оно, например, обладает свойством коммутативности, в отличии от оператора присваивания.


                                    Очевидно, что вы эту разницу не видите, а, значит, проблема есть.


                                    А ваш пассаж в духе "А чего добился ты?", вообще, не самым удачным образом вас характеризует.

                                    • –2
                                      image
                                      Проблем восприятия не вызывает, это формула расчета, но x = 2y+1 это оказывается равенство, а ни зависимость x от y.

                                      Проблема действительно высосана из пальца.
                                      • +3
                                        а почему равенство не может быть частным случаем зависимости?
                                        • +3

                                          Формула расчета и есть равенство, никакого противоречия тут нет. Но равенство это не то же самое, что и присвоение.

                                          • –6
                                            Верно. Равенство (операция сравнения) не тоже самое, что присвоение. Но когда в физике/математике нужно «посчитать», то мы используем = как присвоение. Алгоритмы какраз «считают», поэтому = должно быть присвоением. = как равенство используется только в решении уравнений и преобразованиях формул, чем алгоритмы не занимаются.
                                            • 0
                                              Как вы не замечаете очевидной разницы между:
                                              1. x = y + 1 в математике и
                                              2. x = y + 1 в программировании?

                                              Попробуйте изменить значение переменной y и все станет ясно. В математике вам еще придется «извращнуться» и ввести множества значений и элементы множества, чтобы «зафиксировать» состояние на тот или иной момент. В программировании итерационная модель состояний (последовательно сменная) уже в голове разработчика.
                                              • 0

                                                о_О У вас очень странное представление о том, что такое алгоритм.

                                                • +2

                                                  Ничего мы в физике никуда не присваиваем. Там это такое же равенство как условие, не более того. Например, F = mg — закон тяготения. Где мы тут что-то чему-то присваиваем? А вычисление — это всего лишь редуцирование к простейшим уровнениям типа x = число или x in {числа}. И тут тоже нет никаких присвоений.

                                                  • 0

                                                    Верно. Вычисления в физике — это просто определение частного случая равенства на основе известных значений переменных. Никаких присваиваний.

                                                    • 0
                                                      Где мы тут что-то чему-то присваиваем?

                                                      В случае вычислений по данной формуле как раз-таки присваиваем.
                                                    • 0
                                                      Верно. Равенство (операция сравнения) не тоже самое, что присвоение. Но когда в физике/математике нужно «посчитать», то мы используем = как присвоение

                                                      откровенно говоря, в мат. контексте x = y + 1 "=" — это не «логическое равно», а объявление функциональной зависимости между x и y.
                                            • +3
                                              Всегда не любил паскаль именно за ":=" для присваивания. Просто потому что это дольше печатать, чем "=".
                                              Конечно, зато в паскалье "=" — это сравнение, но как-то так получается, что в коде я на порядок чаще делаю присвоение.
                                              • +4

                                                Дольше печатать? ORLY? Теперь, когда вы пишете на каком-нибудь С#, печатать нужно меньше? Куда вы деваете освободившееся время?

                                                • +3

                                                  На отладку ошибок этим вызванных </troll-mode>

                                                  • +1
                                                    Таки да, два символа печатать дольше, чем один.
                                                    Я не говорю, что суммарно в коде нужно печатать меньше… хотя благодаря современным IDE, пожалуй что и вправду меньше. Если я пишу на C#, то в основном нажимаю ctrl+space :)
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                      • 0

                                                        Минус у .equals(T o) ровно один — возможность NPE.
                                                        А всё остальное — специфика языка, где из-за одних компромиссов возникают некоторые минусы и некоторые плюсы. В частности запрет переопределения операторов — имхо скорее благо.

                                                        • +3
                                                          Равенство — это довольно размытое понятие (и для строк в особенности), чтобы уложить его в один оператор. Равны ли друг другу строки «Елка», «елка», «ёлка», u«ёлка» и U«ёлка»? Всё зависит от контекста — считаем ли бы эквивалентными строки, записанные одинаковыми символами, но в разной кодировке, или в разном регистре, или если символы одинаковые, но записаны разными codepoints, или символы разные, но для читателя оба написания эквивалентны (е/ё)? В одной и той же программе, но в разных местах, запросто может понадобиться разное поведение, и никакого поведения «по-умолчанию» может и не быть. А если вспомнить ещё и равенство ссылок vs равенство значений?

                                                          Так что я всегда с большой опаской гляжу на места в коде, где строки (или другие сложные структуры) сравниваются операторами "==", "<", ">", а не функциями вроде Еquals() или Compare(), или другими специализированными предикатами. Нереально впихнуть в простые операторы все мыслимые группы эквивалентности и способы упорядочивания — это годится только для простейших типов вроде enum-ов и чисел, и даже там оператор сравнения моментально ломается с переходом к плавающей точке.
                                                          • 0

                                                            В подавляющем большинстве случаев требуется максимально простое, быстрое и тупое сравнение, которое 1) даёт предсказуемый результат для ASCII-символов, 2) даёт true для в точности одинаковых строк, 3) желательно даёт false для хоть сколько-нибудь различных строк, 4) не зависит ни от каких культур. И да, в C# оператор == для строк именно таков.

                                                            • 0
                                                              Я согласен, что это наиболее частый случай, может даже и подавляюще частый. Но случаев, когда тупое бинарное сравнение строк не подходит, но его бездумно используют ибо привыкли к встроенному — тоже очень много, и багов от этого я видел немало. Именно поэтому я не отрицаю оператор "==", а гляжу на него с подозрением: каждый раз, как он попадается, приходится делать паузу и думать — действительно ли его использовали как надо, или просто по привычке воткнули?
                                                              • 0

                                                                Я боюсь, что использование Equals решает эту проблему, только если требовать обязательность указания метода сравнения, но в Java, если не ошибаюсь, этого нет. Лично я считаю, что если уж делать всё совсем правильно, то должны быть два разных типа для строк — "культурные" и "бескультурные", чтобы их нельзя было незаметно перепутать. Тогда для второго типа можно иметь обычное простое сравнение, а для первого надо обязательно требовать передачу способа сравнения. Но и в текущей ситуации есть свои плюсы.

                                                                • 0

                                                                  В Java есть интерфейсы Comparable и Comparator. Именно они предназначены для функционального, подходящего к конкретной ситуации сравнения.
                                                                  метод equals в среднем даёт понимание — является ли предложенный элемент эквивалентным текущему. Не равным, а эквивалентныым и это уже зависит только от класса и заложенного в него алгоритма эквивалентности.
                                                                  Почему там не стали вешать этот метод на ==:


                                                                  • в Java сознательно отказались от перегрузки операторов. Решение может кому-то нравиться, кому-то не нравится, это банальная особенность языка. Но это не даёт возможности переопределения функции равенства
                                                                  • оператор == когда-то определил как оператор строгого равенства — равенства по ссылке. Он отвечает только на вопрос — тот же это объект или нет. Он никогда не отвечает на вопрос, похож ли этот объект на тот.
                                                    • 0
                                                      А на каком ЯП вы сейчас программируете? уверен что есть ЯП где надо печатать меньше
                                                      • +1
                                                        Malbolge.
                                                        • 0
                                                          This Malbolge program displays «Hello World!», with both words capitalized and exclamation mark at the end.
                                                          (=<`#9]~6ZY32Vx/4Rs+0No-&Jk)«Fh}|Bcy?`=*z]Kw%oG4UUS0/@-ejc(:'8dc

                                                          print("Hello World!")
                                                    • +1
                                                      Из современных языков — присваивание через ":" есть в maxmia, а через "<-" — в R
                                                      • 0

                                                        я в скале иногда определяю := для присвоения по значению. Поведение = там изменить нельзя — оно всегда по ссылке для ссылочных типов.
                                                        Ещё забавно: в go есть :=, причём там используется наоборот — для объявления переменной с присвоением значения, a = для присвоения!

                                                        • –2
                                                          Go
                                                          • +1

                                                            Особенно хорошо, когда пытаются сделать res, err := ..., а одна из переменных уже была объявлена..

                                                        • +11

                                                          Всегда любил Паскаль именно за ":=" для присваивания. Да, чуть больше писать, зато никакой неоднозначности. И куча времени и нервов сэкономлена на глупых ошибках, и не надо потом мучительно гадать, что имел в виду писатель: if (a = fn(x))... — то ли это ошибка, то ли автор ленив или эстет.

                                                          • +4

                                                            Справидливости ради, проблему с if (a = fn(x))... можно решить просто убрав возвращаемый результат у оператора присваивания.

                                                            • +2
                                                              Этак у нас какой-нибудь ABAP останется, если все прикольные и удобные, хотя и немного опасные штуки убрать.
                                                              • +1

                                                                Конкретно это штуку и так, не редко, убирают. В Python нет такой возможности, потому что присваивание это statement, а не expression (как раз, в комментирии ниже упомянули). В Swift, та же самая петрушка.

                                                            • 0

                                                              Тогда нельзя будет писать a = b = c, что иногда используется.


                                                              А проблему с if можно решить явным требованием типа bool у выражения.

                                                              • +2

                                                                При условии, что вы не пытаетесь присваивать значение типа bool.

                                                                • 0

                                                                  Почему нельзя? Просто сделайте такую грамматику, что a = b = c — это не assign("a", assign("b", "c")), а assign(["a", "b"], "c"). В Python присваивание — это может и statement, но писать a = b = c всё равно можно:


                                                                  >>> ast.dump(ast.parse("a = b = c"))
                                                                  "Module(body=[Assign(targets=[Name(id='a', ctx=Store()), Name(id='b', ctx=Store())], value=Name(id='c', ctx=Load()))])"
                                                                • +1
                                                                  Или потребовав явного слова на это, например

                                                                      if (take a = fn(x))
                                                                  


                                                                  польза — такие случаи (нечастые, но полезные) будут явно выделяться в коде.
                                                                • 0
                                                                  в питоне потому присваивание внутри if и запретили
                                                                  • +1
                                                                    while, for?
                                                                    Конструкция вида while(r = next()) удобна и позволяет обойтись без дублирования кода.
                                                                    • +1

                                                                      В такой ситуации предполагается использовать for:


                                                                      for r in iterable:
                                                                          pass

                                                                      Здесь аналог next является частью контракта итератора.

                                                                      • 0
                                                                        в питоне нет классического for, только для итератора
                                                                        про while уже написали
                                                                        • 0

                                                                          Переписывается в while(true) .. break. Да, на 1 строчку больше писать, но выгоды на мой взгляд намного больше.

                                                                          • 0

                                                                            While (true)… break — это костыль и изнасилование здравого смысла.

                                                                            • 0

                                                                              Да, в правильном сценарии у вас есть нормальный итератор, где вы просто пишете for value in mystream.filter_map(|x| x.read()) { ... }. Для меня изнасилованием является попытка присовокупить и еще что-то посчитать. Тот же Липперт считает это одним из главных косяков при проектировании C#. Также интересной информацией может быть тот факт, что во всех новых языках присваивание это таки statement.

                                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                                • 0

                                                                                  Да, конечно, и я тоже пользуюсь такой конструкцией. Но же просто замаскированный goto.

                                                                                  • 0
                                                                                    Он не плох сам по себе (тихо-тихо, положите помидоры и тапки).
                                                                                    В данном случае мы получаем более-менее структурированный goto, который не создает спагетти-кода (ну ладно, почти, одно перекрещивание).
                                                                                    • +1
                                                                                      На самом деле страсти вокруг goto — это карго-культ какой-то.

                                                                                      Проблемы с goto начинаются, когда вы начинаете использовать его для перезода «вверх» по коду. Пока все переходы ведут «вниз» — на читабельность это влияет относительно слабо.
                                                                                      • 0
                                                                                        Не обязательно строго «вниз», местами приходилось делать конструкции вида

                                                                                        if (плохо1) {
                                                                                          cleanup: почистить;
                                                                                          return;
                                                                                        }
                                                                                        ...
                                                                                        if (плохо2) {
                                                                                          goto cleanup;
                                                                                        }
                                                                                        


                                                                                        С другой стороны, вход куда-то внутрь тела цикла даже ниже по ходу может быть жуткой диверсией.

                                                                                        Но тем не менее правила в следующем духе могут быть вполне приемлемыми для 99% случаев:
                                                                                        Допускаются переходы:
                                                                                        1. Как реализация отсутствующих в C break, continue с меткой.
                                                                                        2. В точку кода, из которой единственный выход — итоговый выход из функции. (Это для таких cleanup.)
                                                                                        3. Вниз по коду, если происходит переход на тот же уровень вложенности или меньший.

                                                                                        • +4
                                                                                          обычно в таких случаях cleanup блок делают внизу функции:
                                                                                          void foo(...) {
                                                                                              resource1 = ...
                                                                                              if (плохо1)
                                                                                                  goto cleanup1;
                                                                                              //...
                                                                                              resource2 = ...
                                                                                              if (плохо2)
                                                                                                  goto cleanup2;
                                                                                              //...
                                                                                          cleanup2:
                                                                                              (очистка ресурса 2);
                                                                                          cleanup1:
                                                                                              (очистка ресурса 1);
                                                                                          }
                                                                                          

                                                                                          такой early return не противоречит позитивному сценарию и в принципе используется достаточно часто.
                                                                                          • +1
                                                                                            На самом деле подобные cleanup легко переносятся в конец функции, где им, собственно, самое место, а пункт 1 является частным случаем пункта 3.
                                                                                            • 0
                                                                                              В Delphi обычная практика:

                                                                                              создали
                                                                                              try
                                                                                               выполнили
                                                                                              finally
                                                                                               разрушили
                                                                                              end;
                                                                                              
                                                                                              гарантированно всё разрушится, безо всяких goto.
                                                                                              • +1

                                                                                                Только теперь вы уже не можете быть уверены в том, как будет исполняться программа. Наличие исключений — это как замаскированный (и ограниченный до «только вверх по стёку») longjump практически из любого места программы. Если есть checked exceptions то у вас хотя бы есть идея о том, что может быть выброшено. Но обычно их нет. Отлично понимаю авторов Rust и Go, у которых нет исключений в традиционном виде, а то что есть не предполагается для использования таким образом. Часто механизм удобен, но идея их использования в сложноотлаживаемых приложениях (у меня в основном embedded) мне не нравится.

                                                                                                • 0
                                                                                                  Только теперь вы уже не можете быть уверены в том, как будет исполняться программа

                                                                                                  Стоит расценивать исключения как один из вариантов исполнения программы, хоть и необычный. Все исключения можно достаточно хорошо запротоколировать и разобраться, что не так. Возможно где-то со временем.
                                                                                                  • 0
                                                                                                    не очень хорошие примеры вы привели. Обработку ошибок в Go не пинает только ленивый. А в rust best practice — функция возвращает Option/Result и использует макро try! для быстрого возврата ошибки. Грубо говоря, это более явная и менее удобная эмуляция тех самых исключений.

                                                                                                    Сами по себе исключения — максимально лаконичный и гибкий инструмент. Не всегда уместный и неявный, и от того сильно недооцененный, на мой взгляд. Именно посредством исключений можно «прокидывать» максимум любой отладочной информации. Разве что в языках с поддержкой исключений я бы предпочел вариант «функция может кидать исключения только если это указано» вместо «функция может кидать исключения если не указано обратное».
                                                                                                    • +1
                                                                                                      Разве что в языках с поддержкой исключений я бы предпочел вариант «функция может кидать исключения только если это указано» вместо «функция может кидать исключения если не указано обратное».
                                                                                                      Не работает. Либо у вас ошибки типа переполнения счётчика бросают исключения и их нужно обрабатывать, чтобы программа скомпилировалась, то есть на каждую полезную строчку у вас оброауется пять строк к обработкой ошибок (а вроде как мы и заводили-то исключения, чтобы от этого избавится), либо у вас получаются «исключительные исключения», которые вроде как бросаются — но не ловятся. Как только вы завели «исключительные исключения» — в них тут же начинают заворачивать всё подряд, так как согласно API ваша функция бросать ислючение типа «файл не читается» не может, а программу как-то скомпилировать всё-таки надо.

                                                                                                      Обработка ошибок в Go и Rust'е, может быть, и не идеальна, но я не могу сказать, что то, что предлагает C#/Java наёжнее, увы.

                                                                                                      Сами по себе исключения — максимально лаконичный и гибкий инструмент. Не всегда уместный и неявный, и от того сильно недооцененный, на мой взгляд.
                                                                                                      То же самое можно сказать про GoTo. Слава богу в современных языках исключения ходят только в одну сторону, это не call/cc, но за счёт того, что это не просто GoTo, а нелокальный GoTo, переходящий из одного модуля в другой разрушения он создаёт те ещё…
                                                                                                      • 0
                                                                                                        Либо у вас ошибки типа переполнения счётчика…
                                                                                                        … что предлагает C#/Java ...

                                                                                                        я говорил скорее про исключения в с++, которые (по идее) являются не следствием ошибки программиста, а сигналом о некорректном состоянии программы/окружения для выполнения операции. А ошибки типа выхода за границы — просто UB.
                                                                                                        То же самое можно сказать про GoTo

                                                                                                        исключения могут быть вложенными. Типа «Измерение не удалось» потому что «Нет соединения с датчиком» потому что «не установлен драйвер ...».
                                                                                                        • 0
                                                                                                          исключения могут быть вложенными. Типа «Измерение не удалось» потому что «Нет соединения с датчиком» потому что «не установлен драйвер ...».
                                                                                                          Прекрасно. Кто всё это будет раскручивать?

                                                                                                          Если ответ «программист, когда увидит логи» — то это и без исключений делается, а если вы думаете что у вас программа измерения пульса будет со всем этим разбираться, то вы явно не видели кода этих программ… они и куда более простые ошибки не обрабатывают.
                                                                                                      • 0

                                                                                                        Rust не ограничен try!.. И с таким подходом я не могу не знать, есть ли у функции «исключения». В go могли бы сделать и лучше. Но, тем не менее, порядок исполнения там весьма ясен. Мне вполне нравится работать в Python, в некоторых случаях оставляя длинные traceback’и как сообщения об ошибках, а в некоторых ловя всё подряд и запихивая в лог. Но я никогда бы не выбрал ни [Micro]Python (ни C++) для embedded, даже если бы мог (если для конкретной ИС есть нужный компилятор) (хотя C++ далеко не за исключения, тем более что их можно отключить): прокидывание «максимума полезной информации» — это последняя вещь, что мне здесь нужна, потому что её ещё надо как‐то достать. А вот однозначный порядок исполнения (хотя и нарушаемый иногда прерываниями) весьма полезен. И Rust мне весьма понравился (хотя я делал на нём один‐единственный проект, только проект сразу был прошивкой для stm’ки).


                                                                                                        Я, наверное, не ясно высказался: я не против ни исключений, ни goto. Я просто вижу, что исключения легко могут быть хуже goto, особенно если отладочную информацию в них не положить.

                                                                                                      • 0
                                                                                                        По исключениям можно поспорить, но важно также то, что finally блок выполняется даже если ты нормальным образом выпрыгиваешь из его тела, даже не при исключении. Исключение можно ловить по типам отдельно.
                                                                                                      • +1
                                                                                                        гарантированно всё разрушится, безо всяких goto.
                                                                                                        Исключения — это как раз и есть «goto на стероидах». Даже хуже longjump'а.

                                                                                                        Да, с ними можно жить и даже писать достаточно надёжные программы, но я до сих пор не уверен, что их наличие упрощает, а не усложняет жизнь.
                                                                                                        • 0
                                                                                                          Все как всегда: есть задача, есть инструменты для нее. Беда начинается, когда инструмент превращается в догмат.
                                                                                    • +1
                                                                                      Так присваивание и сравнение в большинстве (известных мне) языков различается, так что никаких проблем. В том же С это = и ==. Проблема получается когда пытаешься писать на 2х языках с разным подходом к этому вопросу, т.к. постоянно путаешь — где какой вариант. Ну и как правильно кто-то заметил — присваивание обычно более частая операция, так что на неё логичнее иметь более короткий вариант.
                                                                                      • +1
                                                                                        Так присваивание и сравнение в большинстве (известных мне) языков различается, так что никаких проблем. В том же С это = и ==. Проблема получается
                                                                                        Проблема получается, когда забываешь написать второй символ "=". Очень частая и сложнонаходимая ошибка.
                                                                                        • 0

                                                                                          Именно в C компилятор такое часто находит, если вы не злоупотребляете скобками и придерживаетесь правила, что при компиляции не должны вылезать никакие предупреждения вообще. И есть много статических анализаторов, та же рекламируемая здесь PVS-Studio.

                                                                                          • +3

                                                                                            В том то и дело, сэкономили один символ на каждое присвоение при печати, и, вот, кто-то вынужден писать инструменты преодоления проблемы, которую можно было не создавать. А вам, мало того, что их нужно использовать, за некоторые нужно и платить.

                                                                                            • +2

                                                                                              Да по-моему "в 2 раза чаще писать -> должно быть в 2 раза короче" — это бред полный, а не аргументация… Можно подумать, что пара тысяч сэкономленных символов сделает написание нетривиальной программы хотя бы на 0.01% быстрее…
                                                                                              На отладку 1 ошибки забытого = в == всё это "сэкономленное" время и уйдёт.

                                                                                              • 0

                                                                                                Просто раньше время было другое… Написание программы занимало много времени, и это считалось нормальным, чтобы программист держал программу в голове. Гораздо проблемнее всегда была проблема с местом, памятью и мегагерцами процессоров. Считалось, что нужно экономить память (и место) везде, где это в целом возможно.

                                                                                          • –1
                                                                                            Да, есть такое. Однако она решается относительно просто. В каком-то из компиляторов (gcc вроде) для использования = в if требовалось добавлять вторые скобки. Т.е. нельзя написать if(a=b), надо if((a=b)). Т.е. просто так опечататься уже не получится.
                                                                                            А с учётом системы возвращения последнего результата действий замена = и == ничего не даст. У меня были случаи, когда на автомате писал == вместо = и долго пытался понять, почему значение не меняется.
                                                                                            • 0
                                                                                              GCC не запрещает if(a=b), но предупреждает о возможной некорректности. И да, за такими предупреждениями надо следить.
                                                                                              • 0
                                                                                                Возможно просто привык уже считать это предупреждение ошибкой…
                                                                                                • 0

                                                                                                  GCC и clang позволяют превратить предупреждения в ошибки компиляции. Хорошая идея так делать, как минимум, на CI сервере.

                                                                                                  • 0

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

                                                                                        • 0

                                                                                          if (a = fn(x))
                                                                                          Это исправили… в Java и некоторых других языках. Там такое выражение сработает исключительно если функция возвращает булево значение. А тот же C о булевом значении как самостоятельном явлении и не подозревает вроде до сих пор (раньше-то вообще не подозревал, а сейчас, если я правильно помню — всё равно считает числом)

                                                                                          • 0

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

                                                                                        • 0

                                                                                          В ABAP "=" это и присваивание, и сравнение, а в новых версиях и инициализация. И ничего, вполне нормально воспринимается, без какого-либо неудобства, из контекста всегда однозначно понятно о чем речь.
                                                                                          Если в ЯП возникают проблемы вроде описанной в статье, то это либо запутанный синтаксис, либо "одаренный" программист

                                                                                          • +2
                                                                                            Автор не прав. Сначала было:
                                                                                            let a = 5
                                                                                            А потом упразднили let :)
                                                                                            • +2
                                                                                              Более того, автор допускает ошибку предпосылки:

                                                                                              неявно подразумевается, что доминирующей причиной существующего порядка организации вещей является историческое происхождение явления.

                                                                                              в реальности, происходящее калибруется по оптимальным траекториям с сильно превосходящим исторические характеристики давлением естественного отбора: даже реальные языки активно мутируют под давлением фактора увеличения гармоничности и удобства использования инструмента под задачи и обстоятельства

                                                                                              касательно самого графического глифа "=" — следует рассматривать визуальную первичность восприятия выражений

                                                                                              в математике, из которой «наследуется смысл символа равно» — знак равенства по сути является не «сравнением», а «декларированием»

                                                                                              аналогом декларирования в функциональном программирования является именно декларирование, а в императивном — не сравнение, а присвоение.

                                                                                              так что абсолютно логично использовать "=" для присвоения, а вот для сравнения правильнее использовать либо «модифицированный под смысл операции сравнения» символ равенства (==), либо «EQ»

                                                                                              есть и более короткое и логичное объяснение. то, что пишется чаще, должно писаться короче.

                                                                                              в импративном коде основа алгоритма — переменные, и гораздо чаще происходит присвоение, чем сравнение. соответственно короткий знак всегда будет побеждать и вытеснять громоздкое и неудобное в процессе эволюции кода
                                                                                              • +1
                                                                                                так что абсолютно логично использовать "=" для присвоения, а вот для сравнения правильнее использовать либо «модифицированный под смысл операции сравнения» символ равенства (==), либо «EQ»

                                                                                                В бейсике и присваивание и сравнение делается через "=", и при этом их не спутать.
                                                                                                • +2

                                                                                                  Правильнее сказать, что там контекст не спутать (особенно в диалектах с LET). А так это три разные операции: императивное присваивание, декларативное утверждение равенства, и сравнение.

                                                                                                  • +1
                                                                                                    Можно пример как это на бейсике?
                                                                                                    • +1
                                                                                                      a = b или let a = b
                                                                                                      if a = b then
                                                                                                      • 0
                                                                                                        let a = b = c

                                                                                                        можно и спутать
                                                                                                        • 0
                                                                                                          Нет, нельзя. Эта конструкция допустима только и только если a — булево и первый знак равенства — присвоение, а второй — сравнение.
                                                                                                          Другое дело, что человек мог описаться, но это ничем не лучше и не хуже путаницы в С типа if (a = b)
                                                                                              • +4

                                                                                                Мне кажется что я привык к = так же как и к десятичной системе счисления.

                                                                                                • +7

                                                                                                  Кажется люди просто путают арифметический синтаксис и синтаксис языков программирования.


                                                                                                  Это типа, как сравнивать английские буквы и латинские. Выглядят одинаково, но при этом правила использования абсолютно другие

                                                                                                  • 0
                                                                                                    И тем не менее языки программирования в той или иной форме используют обычные арифметические конструкции и конструкции из естественных языков.
                                                                                                    Почему бы не использовать их правила и ограничения?
                                                                                                    Для программирования же не придумали свой знак + и -.
                                                                                                    = и == действительно так себе конструкция, от того, что к ней все привыкли, не делает ее логичнее.
                                                                                                  • 0
                                                                                                    Здесь и далее- мои рассуждения об этом как человека весьма далёкого от программирования
                                                                                                    hlfx.ru/forum/showthread.php?threadid=4646&postid=149556#post149556
                                                                                                    • –1

                                                                                                      Сейчас оператору := нашли идеальное применение в Go — для объявления переменной, совмещенной с инициализацией (и выводом типа). С точки зрения краткости это самое то. Но следующий шаг не сделали: можно было разрешить этот оператор внутри выражений:
                                                                                                      ``` x := (123 + (y:= a*b))


                                                                                                      ИМХО это было бы весьма в сишном хакерском стиле :)
                                                                                                      • +2

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

                                                                                                      • +2
                                                                                                        Задело про Лисп и переменные. Мы в институте учили Лисп как строго функциональный язык, есть (lambda ...) и восемь стартовых функций, и всё, и вертись в параболе, никаких переменных и присваиваний, никаких циклов, только (хвостовая по возможности) рекурсия — круть была неимоверная. А потом я увидел, как на Лиспе пишут как на сях.
                                                                                                        • +1
                                                                                                          Мы в институте учили Лисп как строго функциональный язык,

                                                                                                          Но это не делает его строго функциональным. Он может реализовывать разные парадигмы. Тем наверное и ценен.

                                                                                                          • +2
                                                                                                            никаких переменных и присваиваний, никаких циклов, только (хвостовая по возможности) рекурсия

                                                                                                            При том, что в Common Lisp, например, нет tail call optimization (из-за динамических областей видимости, насколько я понимаю). Без циклов на нём никак.

                                                                                                            • 0
                                                                                                              Ну, студенческие проекты обычно не выбирали даже той кучи, которая была доступна в mulisp (один 16-битный сегмент), разве что автор затеял в какой-то функции в одном выражении дважды вызывать её саму. Да и честно, о хвостовой рекурсии я узнал только спустя несколько лет после окончания института. Так что только рекурсия, только хардкор. И ведь решали!
                                                                                                            • +4
                                                                                                              Программист на Фортране может написать программу на Фортране на любом языке программирования
                                                                                                            • +2
                                                                                                              • 0
                                                                                                                Можно объяснить, в чем в той цитате юмор?
                                                                                                            • 0

                                                                                                              Мне кажется, с x = x + 1 проблема в том, что математика не рассматривает понятие "состояние". Любая динамика раскладывается по оси времени в статичный график, и все утверждения делаются относительно статичных моментов или их последовательности. Понятно, что в один и тот же момент x не может быть равно x + 1. Ближе всего понятие суммы по i от 1 до n, но в математике не рассматривают детали реализации, как i принимает свои значения.

                                                                                                              • 0
                                                                                                                ещё как рассматривается.
                                                                                                                есть куча методов основанных на итерациях и т.д.
                                                                                                                • 0
                                                                                                                  В «методах, основанных на итерациях», как правило, заводится целый выводок переменных, но каждая из них имеет только одно какое-то значение…
                                                                                                                  • 0
                                                                                                                    Напишите математическим языком, как на некоторой итерации вычислить следующее значение i.