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

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 год.
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну, и что?
Реклама
Комментарии 173
  • +19
    В переводе упущена отличнейшая сноска:
    «BCPL later adding a floating point keyword. And when I say “later”, I mean in 2018.»
    • +3
      Просто отлично. Спасибо, добавил!
    • +11
      Напомнило историю про современные технические достижения и ширину крупа лошади.
      • +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
                                                      Если я пишу на C#, то в основном нажимаю ctrl+space :)

                                                      В Дельфи есть тоже самое, если рассматривать его синтаксис близким к паскалю из-за ":=".

                                                      Куда более убийстввенным было для меня сравнение строк в яве через .Equals(), тут Дельфийские и Шарповские "="/"==" вспоминаются со слезами.
                                                      • 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 уже написали
                                                                    • +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

                                                                          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.
                                                                                                    • 0
                                                                                                      в формулах k = 1..N например.
                                                                                                      но чаще словами пишут.
                                                                                                      в методах ньютона для диффуров все вполне себе понятно написано
                                                                                                      • 0
                                                                                                        Так это не состояние, это список возможных значений. А как k их принимает, как одно значение меняется на другое, это не рассматривается.
                                                                                                        • 0
                                                                                                          вот, например, в методе гауса есть состояние.
                                                                                                          там даже полноценные циклы :)
                                                                                                          • 0
                                                                                                            Я боюсь вы плохо помните как это описывается в математике. Посмотрите хотя бы в Википедию. Да, шагов там много, но все переменные получают значение один раз.

                                                                                                            Вот все эти "а с крышечкой", "a со штрихом", "a с кришечкой и n штрихами" — они как раз оттуда.

                                                                                                            Максимум, что бывает в математике — это пролог-переменные, которые могут в одной «ветке рассуждений» получать значение, но при обнаружении противоречий — терять его.

                                                                                                            Причём в отличие от пролога, где при необнаружении противоерчия переменные могут-таки получить серию «решений» с разными значениями в момент получения неопределённости в математике возникают все эти штрихи и x1, x2, etc.
                                                                                                            • 0
                                                                                                              ну да. в математике выглядит иначе.
                                                                                                              чаще в стиле:
                                                                                                              на следующем шаге примем S равным f(x_n) ну и в таком духе
                                                                                                • 0
                                                                                                  Всем, кто говорит, что в математике "=" означает равенство «навсегда», вот вам простой пример:

                                                                                                  Пусть X = 2, Y = 2. Если X + Y = 5, то 2 + 2 = 5, что неверно. Из этого следует, что X+Y ≠ 5.

                                                                                                  Скажите, в этом нехитром упражнении, сколько раз я использовал "=" в странном смысле?
                                                                                                  • 0
                                                                                                    2 + 2 = 5
                                                                                                    А что тогда "=" означает здесь?
                                                                                                    • 0
                                                                                                      Предположение. Если мы считаем, что X+Y=5, то оказывается, что 2+2=5, а второе утверждение ложное, значит и первое утверждение так же ложное.

                                                                                                      Два равно, и оба ложные, и вселенная всё ещё не взорвалась (повторно).
                                                                                                      • 0
                                                                                                        Предположение
                                                                                                        Тогда X = 2 это тоже предположение, т.е. уже не верно что X + Y = 5 и 2 + 2 = 5 это одно и тоже
                                                                                                        • 0
                                                                                                          Окей. Но если мы становимся на эту позицию, то что остаётся от математики?

                                                                                                          Если X=2, и Y=2, то X+Y=4. Эти знаки равно что обозначают?
                                                                                                          • 0
                                                                                                            Равенство.
                                                                                                            Если мы знаем что X = 2, Y = 2, то X + Y = 5 это утверждение не истинно, также как не истинно и 4=5.
                                                                                                    • –1
                                                                                                      X + Y = 5
                                                                                                      Это не валидно в математике, ошибка в определении задачи
                                                                                                      • +2
                                                                                                        Почему не валидно? Есть куча доказательств от противного, и в них всегда приходят к противоречию, это означает, что часть утверждений (со знаком равно) не являются истинными.
                                                                                                        • +1
                                                                                                          часть утверждений (со знаком равно) не являются истинными
                                                                                                          это я и имел ввиду :)
                                                                                                      • 0
                                                                                                        Скажите, в этом нехитром упражнении, сколько раз я использовал "=" в странном смысле?
                                                                                                        Вы уж скажите что вы тут понимаете под «странным» смыслом.

                                                                                                        Как я уже писал «математическое» присваивание у нас есть только в Prolog'е и близких к нему языках. И вот как раз на нём мало кто пишет — даже из тех, кто, вроде как, предпочитает «функциональщину».
                                                                                                        • 0
                                                                                                          Ни одного. Сделали неверную предпосылку, только и всего.
                                                                                                          • +1
                                                                                                            Ни разу. «Пусть X = 2» — это не присваивание, это логическое утверждение «считаем истинным, что X = 2». Проверка: поменяем все операнды местами:

                                                                                                            Пусть 2 = X, и 2 = Y. Если 5 = X + Y, то 5 = 2 + 2, что неверно. Из этого следует, что 5 ≠ X+Y.

                                                                                                            Хоть и запись непривычна, смысл выражения и вывод не поменялись. С присваиванием такое бы не прошло.
                                                                                                          • 0
                                                                                                            ИМХО если ограничиться исходным кодом для компилятора, то обозначение присваивания дело привычки. А вот как быть, если нужно написать статью (нпр., на Хабр) с описанием алгоритма, мат.доказательством его корректности и теоретической оценкой сложности для наихудшего случая? В математике знак "=" не присваивание, а равенство. И тут возможна путаница между псевдокодом описания алгоритма, листингом его реализации на конкретном ЯП и мат. формулами. Кроме того, даже в исходном коде для компилятора бывает нужно добавить комментарий с мат.формулой. Конечно, опытный программист всегда по контексту поймет, что в данном месте подразумевается под "=". А вот ученик не всегда. Поэтому ИМХО для присваивания удобнее использовать символ/комбинацию символов, отличный от "=".
                                                                                                            • 0
                                                                                                              > В математике знак "=" не присваивание, а равенство.

                                                                                                              В предложениях типа «пусть y = 2x» это именно присвоение (что обозначено дополнительным «пусть» («обозначим» и т.п.), и ещё надо уточнять по контексту, какого из знаков — x или y — ещё не было). В том и дело, что в математике роль конкретного '=' определяется окружающими словами.

                                                                                                              И даже в конструкциях вида «x=x+1» надо отличать, а не идёт ли тут поиск того x, при котором это справедливо (в обычной арифметике — бесконечность любого вида, в булевской — 1, и ещё есть вариантов).
                                                                                                              • 0
                                                                                                                В предложениях типа «пусть y = 2x» это именно присвоение
                                                                                                                Если это присвоение то «x» уже должен быть определён, а мы можем определить его позже
                                                                                                                • 0
                                                                                                                  > Если это присвоение то «x» уже должен быть определён

                                                                                                                  Нет, без контекста нельзя уточнить, что определено — x или y. Один определён, второй определяется через него и получает значение. Оба направления присвоения активно используются.

                                                                                                                  (Кстати, именно под влиянием языков программирования я начал в своих конспектах в подобных случаях явно писать направление назначения — в данном случае x := 2y или x =: 2y. Очень помогало потом разбирать.)
                                                                                                                • 0
                                                                                                                  В математике: «имеет значение».
                                                                                                                  В программировании: «присвоить значение».
                                                                                                                  Имеет значение, вычисляемое как… (а как она его получила, неважно).
                                                                                                                  Присвоить значение, вычисленное как… (в результате чего станет верным выражение «имеет значение»).
                                                                                                                  • 0
                                                                                                                    В предложениях типа «пусть y = 2x» это именно присвоение


                                                                                                                    ИМХО нет!: «пусть y = 2x» можно перевести в слова (что часто и делают, нпр., на лекциях и читая доклады): пусть y равен 2x. Попробуйте перевести «пусть y присвоить 2x» — как-то не по-русски получается ;)
                                                                                                                    • 0
                                                                                                                      В предложениях типа «пусть y = 2x» это именно присвоение (что обозначено дополнительным «пусть»

                                                                                                                      Не согласен. Присвоение некоммутативно, в отличие от равенства. Утверждение типа "пусть" или "предположим" накладывает ограничение на x и y. Если что-то из них задано, то ограничение позволяет вычислить вторую переменную.
                                                                                                                      Если же неизвестно ни одного ни другого, то утверждение вида "… = ..." всё равно имеет смысл, в отличие от присваивания.


                                                                                                                      Вот реальный пример: надо найти массу по весу тела.
                                                                                                                      Я напишу: "по закону Ньютона P = mg, поэтому m = P/g" Присвоением можно разве что вторую формулу назвать (но я её всё равно считаю частным случаем равенства)