Это немного субъективно, но я надеюсь получить более четкое представление о том, какие факторы делают оператор понятным для использования по сравнению с тупым и трудным. Недавно я рассматривал языковые схемы, и одна проблема, на которую я всегда обращаю внимание, это когда нужно сделать какую-то ключевую операцию в языке оператором, а когда использовать ключевое слово или функцию.
Haskell несколько печально известен этим, поскольку пользовательские операторы легко создавать, и часто новый тип данных поставляется с несколькими операторами для использования на нем. Например, библиотека Parsec содержит множество операторов для объединения парсеров, например, с самоцветами, >.
и .>
я даже не могу вспомнить, что они значат прямо сейчас, но я помню, что с ними было очень легко работать, как только я запомнил, что они на самом деле имеют в виду. leftCompose(parser1, parser2)
Был бы вызов функции такой, как было бы лучше? Конечно, более многословный, но более понятный в некоторых отношениях.
Перегрузки операторов в C-подобных языках представляют собой аналогичную проблему, но сталкиваются с дополнительной проблемой перегрузки значения знакомых операторов, таких как +
необычные новые значения.
На любом новом языке это может показаться довольно сложной задачей. Например, в F # приведение использует математически выведенный оператор приведения типов вместо синтаксиса приведения стиля C # или многословного стиля VB. C #: (int32) x
VB: CType(x, int32)
F #:x :> int32
Теоретически новый язык может иметь операторы для большинства встроенных функций. Вместо def
или dec
или var
для объявления переменной, почему нет ! name
или @ name
или что-то подобное. Это, безусловно, сокращает объявление, сопровождаемое связыванием: @x := 5
вместо declare x = 5
или let x = 5
Большинству кода потребуется много определений переменных, так почему бы и нет?
Когда оператор понятен и полезен, а когда скрыт?
источник
+
для конкатенации строк или<<
для потоков). С другой стороны, в Haskell оператор является либо просто функцией с произвольным именем (то есть не перегруженной), либо частью класса типов, что означает, что, хотя он полиморфен, он выполняет одинаковую логическую функцию для каждого типа, и даже имеет такую же подпись типа. Так>>
это>>
для каждого типа и никогда не будет немного сдвиг.Ответы:
с точки зрения разработки общего языка не должно быть никакой разницы между функциями и операторами. Можно описать функции как префиксные операции с любой (или даже переменной) арностью. А ключевые слова можно рассматривать как просто функции или операторы с зарезервированными именами (что полезно при разработке грамматики).
Все эти решения в конечном итоге сводятся к тому, как вы хотите, чтобы нотация читалась, и что, как вы говорите, субъективно, хотя можно сделать некоторые очевидные рационализации, например, использовать обычные инфиксные операторы для математики, так как все их знают
Наконец, Дейкстра написал интересное обоснование математических обозначений , которые он использовал, что включает в себя хорошее обсуждение компромиссов между инфиксными и префиксными нотациями.
источник
Для меня оператор перестает быть полезным, когда вы больше не можете читать строку кода вслух или в своей голове, и это имеет смысл.
Например,
declare x = 5
чтение как"declare x equals 5"
илиlet x = 5
может быть прочитано как"let x equal 5"
, что очень понятно, когда читается вслух, однако чтение@x := 5
читается как"at x colon equals 5"
(или,"at x is defined to be 5"
если вы математик), что не имеет смысла вообще.Поэтому, на мой взгляд, используйте оператор, если код может быть прочитан вслух и понят разумным человеком, не знакомым с языком, но используйте имена функций, если нет.
источник
@x := 5
это трудно понять - я все еще читаю это вслух себеset x to be equal to 5
.:=
имеет более широкое применение в математической нотации вне языков программирования. Кроме того, такие заявленияx = x + 1
становятся немного абсурдными.:=
это задание. Несколько языков действительно используют это. Smalltalk, я думаю, был, пожалуй, самым известным. Вопрос о том, должны ли присваивание и равенство быть отдельными операторами, - это совершенно другой набор червей, один из которых, вероятно, уже затронут другим вопросом.:=
использовалось в математике. Единственное определение, которое я нашел для него, было «определено как», поэтому@x := 5
будет переведено на английский язык"at x is defined to be 5"
, что для меня все еще не так важно, как следовало бы.Оператор понятен и полезен, когда знаком. И это, вероятно, означает, что перегрузка операторов должна выполняться только тогда, когда выбранный оператор находится достаточно близко (как в форме, приоритетности и ассоциативности), как уже сложившаяся практика.
Но копать немного дальше, есть два аспекта.
Синтаксис (инфикс для оператора, а не префикс для синтаксиса вызова функции) и наименование.
Что касается синтаксиса, то есть другой случай, когда инфикс является более понятным, чем префикс, что может оправдать усилия по ознакомлению: когда вызовы объединены в цепочку и вложены.
Сравните
a * b + c * d + e * f
с+(+(*(a, b), *(c, d)), *(e, f))
или+ + * a b * c d * e f
(оба они доступны для тех же условий, что и инфиксная версия). Тот факт, что+
отдельные термины вместо длинного предшествуют одному из них, делают его более читабельным в моих глазах (но вы должны помнить правила приоритета, которые не нужны для синтаксиса префиксов). Если вам нужно сложить вещи таким образом, долгосрочная выгода будет стоить затрат на обучение. И вы сохраняете это преимущество, даже если вы называете своих операторов буквами вместо использования символов.Что касается именования операторов, я вижу несколько преимуществ в использовании чего-то другого, кроме установленных символов или ясного имени. Да, они могут быть короче, но они действительно загадочны и будут быстро забыты, если у вас нет веских оснований уже знать их.
Третий аспект, если вы выбираете POV для языкового дизайна, заключается в том, должен ли набор операторов быть открытым или закрытым - можете ли вы добавить оператор или нет - и если приоритет и ассоциативность должны задаваться пользователем или нет. Первым делом я хотел бы быть осторожным и не предоставлять пользователю заданный приоритет и ассоциативность, в то время как я был бы более открыт при наличии открытого набора (это увеличило бы толчок к использованию перегрузки оператора, но уменьшило бы использование плохого). просто воспользоваться синтаксисом инфикса, где это полезно).
источник
Операторы как символы полезны, когда они имеют смысл.
+
и-
, очевидно, являются сложением и вычитанием, например, поэтому почти каждый язык использует их в качестве своих операторов. То же самая со сравнением (<
и>
учат в начальной школе, а также<=
и>=
интуитивные расширения основных сравнений , когда вы понимаете , что нет никакой подчеркнутого-сравнения клавиши на стандартной клавиатуре.)*
и/
менее сразу видно, но они используются повсеместно , и их положение на цифровой клавиатуре прямо рядом с+
и-
ключей помогает обеспечить контекст. C<<
и>>
находятся в одной категории: когда вы понимаете, что такое сдвиг влево и вправо, не так уж сложно понять мнемонику за стрелками.Затем вы переходите к некоторым действительно странным вещам, которые могут превратить код C в нечитаемый суп-оператор. (Выбор C здесь, потому что это очень тяжелый операторный пример, синтаксис которого почти всем знаком, либо с помощью C, либо с потомками.) Например,
%
оператор. Все знают, что это такое: это знак процента ... верно? За исключением C, он не имеет ничего общего с делением на 100; это оператор модуля. Это имеет нулевой смысл мнемонически, но это так!Еще хуже - логические операторы.
&
как и имеет смысл, за исключением того, что есть два разных&
оператора, которые делают две разные вещи, и оба синтаксически допустимы в любом случае, когда один из них допустим, даже если только один из них действительно имеет смысл в любом данном случае. Это просто рецепт путаницы! Операторы or , xor и not еще хуже, поскольку они не используют мнемонически полезные символы и не являются согласованными. (Нет оператора double- xor , а логический и побитовый не используют два разных символа вместо одного символа и удвоенной версии.)И что еще хуже, операторы
&
и*
используются повторно для совершенно разных вещей, что усложняет синтаксический анализ как для людей, так и для компиляторов. (ЧтоA * B
означает? Это полностью зависит от контекста:A
тип или переменная?)По общему признанию, я никогда не разговаривал с Деннисом Ритчи и спрашивал его о дизайнерских решениях, которые он принимал на языке, но во многом из-за этого философия операторов была «символами ради символов».
Сравните это с Pascal, который имеет те же операторы, что и C, но с другой философией их представления: операторы должны быть интуитивно понятными и легко читаемыми. Арифметические операторы одинаковы. Оператор модуля - это слово
mod
, потому что на клавиатуре нет символа, который явно означает «модуль». Логические операторыand
,or
,xor
иnot
, и есть только один друг; компилятор знает, нужна ли вам логическая или побитовая версия, основываясь на том, работаете ли вы с логическими или числовыми значениями, устраняя целый класс ошибок. Это делает код на Паскале намного проще для понимания, чем код на C, особенно в современном редакторе с подсветкой синтаксиса, поэтому операторы и ключевые слова визуально отличаются от идентификаторов.источник
and
иor
все время. Однако, когда он разработал Pascal, он сделал это на мэйнфрейме Control Data, в котором использовались 6-битные символы. Это вынудило решение использовать слова для многих вещей по той простой причине, что в наборе символов просто не было достаточно места для символов и что-то вроде ASCII. Я использовал и C, и Pascal, и нахожу C значительно более читабельным. Вы можете предпочесть Паскаль, но это дело вкуса, а не факта.|
(и, соответственно,||
) для или имеет смысл. Вы также видите это все время для чередования в других контекстах, таких как грамматики, и похоже, что это разделяет два варианта.Я думаю, что операторы гораздо более читабельны, когда их функция близко отражает их знакомое математическое назначение. Например, стандартные операторы, такие как +, - и т. Д., Более удобочитаемы, чем сложение или вычитание при выполнении сложения и вычитания. Поведение оператора должно быть четко определено. Например, я могу допустить перегрузку значения + для конкатенации списков. В идеале операция не будет иметь побочных эффектов, возвращая значение, а не изменяя.
Я боролся с горячими операторами для таких функций, как сворачивание. Очевидно, что больше опыта с ними облегчит это, но я нахожу
foldRight
более читабельным, чем/:
.источник
fold
достаточно часто, чтобы оператор экономил много времени. Это также может быть причиной того, что LISPy (+ 1 2 3) кажется странным, но (добавьте 1 2 3) меньше. Мы склонны считать операторы строго бинарными.>>.
Операторы стиля Parsec могут быть глупыми, но, по крайней мере, первое, что вы делаете в Parsec, это комбинирование синтаксических анализаторов, так что операторы для него получают много пользы.Проблема с операторами заключается в том, что их только небольшое количество по сравнению с количеством чувственных (!) Имен методов, которые можно использовать вместо них. Побочным эффектом является то, что определяемые пользователем операторы имеют тенденцию вводить много перегрузок.
Конечно, строку
C = A * x + b * c
легче писать и читать, чемC = A.multiplyVector(x).addVector(b.multiplyScalar(c))
.Или, конечно, написать проще, если в вашей голове есть все перегруженные версии всех этих операторов. И вы можете прочитать его позже, исправляя вторую ошибку.
Теперь, для большей части кода, который будет работать там, это нормально. «Проект проходит все тесты, и он работает» - для многих программных продуктов это все, что мы когда-либо хотели.
Но все работает иначе, когда вы работаете с программным обеспечением, которое идет в критические области. Безопасность критических вещей. Безопасность. Программное обеспечение, которое удерживает самолеты в воздухе или поддерживает работу ядерных объектов. Или программное обеспечение, которое шифрует ваши конфиденциальные электронные письма.
Для экспертов по безопасности, которые специализируются на аудите кода, это может стать кошмаром. Сочетание изобилия пользовательских операторов и перегрузок операторов может оставить их в неприятной ситуации, когда им трудно понять, какой код будет запущен в конечном итоге.
Итак, как указано в исходном вопросе, это очень субъективный предмет. И хотя многие предложения о том, как должны использоваться операторы, могут звучать совершенно чувственно, комбинация лишь нескольких из них может создать много проблем в долгосрочной перспективе.
источник
Полагаю, самый крайний пример - APL, следующая программа - «игра в жизнь»:
И если вы сможете понять, что все это значит, не потратив пару дней со справочным руководством, тогда удачи вам!
Проблема в том, что у вас есть набор символов, которые почти все интуитивно понимают:
+,-,*,/,%,=,==,&
и остальные, которые требуют некоторого объяснения, прежде чем они могут быть поняты, и, как правило, специфичны для конкретного языка. Например, нет очевидного символа для указания, какой элемент массива вы хотите, [], () и даже "." были использованы, но в равной степени не существует очевидного ключевого слова, которое вы могли бы легко использовать, поэтому необходимо предусмотреть разумное использование операторов. Но не слишком много из них.
источник