Чи чіткіші оператори для читання, ніж ключові слова чи функції? [зачинено]


14

Це трохи суб'єктивно, але я сподіваюся отримати більш чітке розуміння того, які чинники роблять оператора зрозумілим у використанні проти тупих і складних. Нещодавно я розглядав мовні конструкції, і одне питання, над яким я завжди кружлю, - це коли зробити якусь ключову операцію на мові оператором і коли використовувати ключове слово чи функцію.

Haskell дещо відомий для цього, оскільки користувацькі оператори легко створювати, і часто новий тип даних поставляється в комплекті з декількома операторами для використання на ньому. Наприклад, бібліотека Parsec постачається з безліччю операторів для комбінування парсерів разом із дорогоцінними каменями, >.і .> я навіть не можу пригадати, що вони означають зараз, але я пам’ятаю, що з ними було дуже просто працювати, коли я запам’ятав, що вони насправді означають. Чи буде виклик функції, такий як leftCompose(parser1, parser2)був кращим? Безумовно, більш багатослівний, але в деяких відношеннях більш чіткий.

Перевантаження операторів на мовах, подібних до С, є подібною проблемою, але пов'язаною з додатковою проблемою перевантаження значення звичних операторів, як, наприклад, +з незвичними новими значеннями.

У будь-якій новій мові це може здатися досить важким питанням. Наприклад, у F # для кастингу використовується оператор кастингу типів, отриманий математично, замість синтаксису відливання стилю C # або багатослівного стилю VB. C #: (int32) xVB: CType(x, int32)F #:x :> int32

Теоретично нова мова може мати операторів для більшості вбудованих функціональних можливостей. Замість defабо decабо varдля оголошення змінної, чому немає ! nameабо @ nameабо що - щось подібне. Це, безумовно, скорочує декларацію з наступним прив'язкою: @x := 5замість declare x = 5або let x = 5 Більшість кодів вимагає багато змінних визначень, так чому б і ні?

Коли оператор зрозумілий і корисний, а коли він затьмарений?



1
Я хотів би, щоб один з 1000 розробників, які коли-небудь скаржилися мені на відсутність Java перевантаження оператора, відповів би на це питання.
smp7d

1
Я думав про APL, коли закінчував вводити питання, але певним чином, що з'ясовує точку і переміщує її на дещо менш суб'єктивну територію. Я думаю, що більшість людей можуть погодитися з тим, що APL втрачає щось у простоті користування через велику кількість операторів, але кількість людей, які скаржаться, коли мова не має перевантаження оператора, безумовно, говорить на користь того, що деякі функції добре підходять для виступають операторами. Між забороненими користувацькими операторами та нічого, окрім операторів, повинні ховатися деякі вказівки щодо практичної реалізації та використання.
CodexArcanum

Як я бачу, відсутність перевантаження оператора заперечує, оскільки вона привілейована нативним типам над визначеними користувачем типами (зауважте, що Java робить це і іншими способами). Я набагато більш неоднозначний щодо персональних операторів у стилі Haskell, які здаються відкритими запрошеннями до проблем ...
штурм

2
@comingstorm: Я фактично думаю, що спосіб Haskell є кращим. Якщо у вас є обмежений набір операторів, який ви можете перевантажувати різними способами, ви часто змушені повторно використовувати операторів у різних контекстах (наприклад, +для конкатенації рядків або <<потоків). З Haskell, з іншого боку, оператор є або просто функцією зі спеціальним іменем (тобто не перевантаженою), або частиною класу типу, що означає, що, хоча він є поліморфним, він робить те ж саме логічне для кожного типу, і навіть має однотипний підпис. Так >>це >>для кожного типу і ніколи не буде трохи зрушення.
Тихон Єлвіс

Відповіді:


16

з точки зору дизайну загальної мови, між функціями та операторами не повинно бути різниці. Можна було описати функції як префіксні операції з будь-якою (або навіть змінною) сутністю. І ключові слова можна розглядати як просто функції або оператори із зарезервованими іменами (що корисно при розробці граматики).

Усі ці рішення зрештою зводиться до того, як ви хочете, щоб нотація читалася, і як, як ви говорите, суб'єктивна, хоча можна зробити кілька очевидних раціоналізацій, наприклад, використовувати звичайні оператори інфікування для математики, як їх усі знають

Нарешті, Дайкстра написав цікаве обґрунтування використовуваних ним математичних позначень , що включає гарне обговорення компромісів нофіксів та префіксів


4
Я хотів би дати вам другий +1 за посилання на папір Dijkstra.
AProgrammer

Чудове посилання, вам довелося опублікувати рахунок PayPal! ;)
Адріано Репетті

8

Для мене оператор перестає бути корисним, коли ви більше не можете читати рядок коду вголос чи в голові, і це має сенс.

Наприклад, 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"якщо ви математик), що зовсім не має сенсу.

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


5
Я не думаю, що @x := 5це важко зрозуміти - я все ще читаю це вголос собі set x to be equal to 5.
FrustratedWithFormsDesigner

3
@ Rachel у цьому випадку :=має ширше використання в математичних позначеннях поза мовами програмування. Також такі заяви x = x + 1стають трохи абсурдними.
ccoakley

3
За іронією долі, я забув, що деяких людей не визнають :=за призначенням. Кілька мов насправді використовують це. Думаю, що Малий розмова, можливо, є найбільш відомим. Будь-яке призначення та рівність мають бути окремими операторами - це зовсім інша банка глистів, напевно, вже охоплене іншим питанням.
CodexArcanum

1
@ccoakley Спасибі, я не знав, що :=використовується в математиці. Одне визначення, яке я знайшов для нього, було "визначене як", тому @x := 5було б перекладено англійською мовою "at x is defined to be 5", що для мене досі не має такого сенсу, як слід.
Рейчел

1
Звичайно Pascal використовується: = як призначення, для відсутності лівої стрілки в ASCII.
Ватін

4

Оператор зрозумілий і корисний, коли він знайомий. І це, ймовірно, означає, що операції з перевантаженням слід робити лише тоді, коли обраний оператор знаходиться в достатній близькості (як у формі, пріоритетності та асоціативності), як уже встановлена ​​практика.

Але копаючи трохи далі, є два аспекти.

Синтаксис (інфікс для оператора на відміну від префікса для синтаксису виклику функції) та іменування.

Для синтаксису є ще один випадок, коли інфікс досить чіткий, ніж префікс, що може вимагати ознайомлення із зусиллями: коли виклики є ланцюговими та вкладеними.

Порівняйте a * b + c * d + e * fз +(+(*(a, b), *(c, d)), *(e, f))або + + * a b * c d * e f(обидва можна проаналізувати в тому ж стані, що і версія інфіксації). Той факт, що +окремі терміни замість того, щоб передувати одному з них довгим способом, роблять його більш читабельним у моїх очах (але ви повинні пам’ятати правила пріоритету, які не потрібні для синтаксису префікса). Якщо вам потрібно скласти речі таким чином, довгострокова вигода повинна бути вартістю навчання. І ви зберігаєте цю перевагу, навіть якщо ви називаєте своїх операторів літерами замість символів.

Щодо імен операторів, я бачу мало переваг у використанні чогось іншого, ніж встановлені символи чи чітка назва. Так, вони можуть бути коротшими, але вони справді загадкові і швидко забудуться, якщо у вас немає вагомих причин вже їх знати.

Третій аспект, якщо ви користуєтесь мовним дизайном POV - це якщо набір операторів має бути відкритим чи закритим - можна додати оператора чи ні - і якщо пріоритет та асоціативність повинні бути визначеними користувачем чи ні. Першим моїм заходом було б бути обережним і не надавати визначений користувачем пріоритет та асоціативність, тоді як я буду більш відкритим щодо наявності відкритого набору (це збільшить натиск на використання перевантаження оператора, але зменшить рівень використання поганого просто скористатися синтаксисом інфіксації там, де це корисно).


Якщо користувачі можуть додавати операторів, чому б не дозволити їм встановити пріоритетність та асоціативність доданих операторів?
Тихон Єлвіс

@TikhonJelvis, Це здебільшого була консервативність, але є деякі аспекти, які слід враховувати, якщо ви хочете мати можливість використовувати це для чого-небудь іншого, крім прикладів. Наприклад, ви хочете прив’язати пріоритет та асоціативність до оператора, а не до заданого члена перевантаженого набору (ви вибрали члена після розбору, вибір не може вплинути на розбір). Таким чином, щоб інтегрувати бібліотеки, використовуючи один і той же оператор, вони повинні узгодити його пріоритет та асоціативність. Перш ніж додавати можливість, я б спробував з’ясувати, чому так мало мов слідкували за Algol 68 (AFAIK жодної) і дозволили визначити їх.
AProgrammer

Ну, Haskell дозволяє визначити довільні оператори та встановити їх пріоритет та асоціативність. Різниця полягає в тому, що ви не можете перевантажувати операторів самі по собі - вони поводяться як звичайні функції. Це означає, що ви не можете мати різні бібліотеки, які намагаються використовувати одного оператора для різних речей. Оскільки ви можете визначити власного оператора, є набагато менше причин використовувати ті ж самі для різних речей.
Тихон Єлвіс

1

Оператори як символи корисні, коли вони інтуїтивно мають сенс. +і -, очевидно, є додаванням і відніманням, наприклад, саме тому майже кожна мова коли-небудь використовує їх як своїх операторів. Те ж сама з порівнянням ( <і >вчать в початковій школі, а також <=і >=інтуїтивні розширення основних порівнянь , коли ви розумієте , що немає ніякої підкресленого-порівняння клавіші на стандартній клавіатурі.)

*і /менш очевидні, але вони використовуються універсально, і їх положення на цифровій клавіатурі поруч із клавішами +та -допомагає забезпечити контекст. C <<і >>знаходяться в одній категорії: коли ти розумієш, що таке зсув вліво і вправо, не дуже складно зрозуміти мнемоніку за стрілками.

Потім ви переходите до справді дивних, речей, які можуть перетворити код C у нечитабельний суп оператора. (Вибираючи тут C, тому що це дуже важкий для оператора приклад, з синтаксисом якого майже всі знайомі, як через роботу з C, так і з мовами нащадків.) Наприклад, тоді %оператор. Усі знають, що це: знак відсотка ... так? За винятком C, це не має нічого спільного з поділом на 100; це оператор модуля. Це має нульовий сенс мнемонічно, але є!

Ще гірше булеві оператори. &як і має сенс, за винятком того, що є два різні &оператори, які роблять дві різні речі, і обидва синтаксично дійсні в будь-якому випадку, коли один з них є дійсним, хоча тільки один з них насправді має сенс у будь-якому випадку. Це просто рецепт плутанини! Оператори or , xor і не є ще гіршими, оскільки вони не використовують мнемонічно корисних символів, а також не є послідовними. (Немає оператора double- xor , а логічний і бітовий не використовувати два різних символи замість одного символу та подвоєної версії.)

І просто , щоб зробити його гірше, &і *оператори отримують повторно використовувати для абсолютно різних речей, що робить мову більш важким для людей , так і компіляторів для синтаксичного аналізу. (Що A * Bозначає? Це повністю залежить від контексту: це Aтип чи змінна?)

Щоправда, я ніколи не розмовляв з Деннісом Річі і не питав його про дизайнерські рішення, які він приймав мовою, але так багато з цього відчуває, що філософія операторів була "символами заради символів".

Порівнюйте це з Pascal, який має ті ж оператори, що і C, але інша філософія в їх представленні: оператори повинні бути інтуїтивними та легкими для читання. Арифметичні оператори однакові. Оператор модуля - це слово mod, оскільки на клавіатурі немає жодного символу, який очевидно означає «модуль». Логічні оператори and, or, xorі not, і є тільки один друг; компілятор знає, чи потрібна вам булева або побітова версія на основі того, чи ви працюєте з булевими чи числами, усуваючи цілий клас помилок. Це робить код Pascal набагато простішим для розуміння, ніж код C, особливо в сучасному редакторі з підсвічуванням синтаксису, тому оператори та ключові слова візуально відрізняються від ідентифікаторів.


4
Паскаль зовсім не представляє іншої філософії. Якщо ви читаєте документи Вірта, він використовував символи для таких речей, як andі orвесь час. Однак, коли він розробив Паскаль, він зробив це на мейнфреймі Control Data, який використовував 6-бітні символи. Це змусило рішення використовувати слова для багатьох речей з тієї простої причини, що набір символів просто не мав майже стільки простору для символів, як щось на зразок ASCII. Я використовував і C, і Pascal, і знаходжу C значно більш читабельним. Ви можете віддати перевагу Паскалю, але це питання смаку, а не факту.
Джеррі Труну

Щоб додати до @JerryCoffin зауваження про Вірт, його наступні мови (Modula, Oberon) використовують більше символів.
AProgrammer

@AProgrammer: І вони нікуди не ходили. ;)
Мейсон Уілер

Я думаю |(і, на розширення ||), для або має сенс. Ви також бачите це весь час для чергування в інших контекстах, наприклад, граматиках, і схоже, що це розділення двох варіантів.
Тихон Єлвіс

1
Перевага ключових ключових слів полягає в тому, що простір у них значно більший, ніж символи. Наприклад, мова в стилі Паскаля може включати в себе оператори як для "модуля", так і для "залишку", а також для цілого чи поділу з плаваючою комою (які мають різні значення, крім типів їх операндів).
supercat

1

Я думаю, що оператори набагато легше читають, коли їх функції тісно відображають їх знайоме, математичне призначення. Наприклад, стандартні оператори, такі як +, - і т. Д., Легше читаються, ніж додавання або віднімання при виконанні додавання і віднімання. Поведінка оператора повинна бути чітко визначена. Я можу допустити перевантаження значення +, наприклад, для конкатенації списку, наприклад. В ідеалі операція не матиме побічних ефектів, повертаючи значення замість мутування.

Однак я боровся з операторами швидкого доступу для таких функцій, як fold. Очевидно, що більше досвіду з ними полегшило б це, але я вважаю, що foldRightце читає більше, ніж /:.


4
Можливо, частота використання входить і в нього. Я б не вважав, що ти foldдосить часто, щоб оператор економив багато часу. Це може бути також тому, що LISPy (+ 1 2 3) здається дивним, але (додати 1 2 3) - це не так. Ми схильні вважати операторів строго бінарними. >>.Оператори стилю Парсека можуть бути глухими, але принаймні головне, що ви робите в Парсеку, - це комбінувати парсери, тому оператори для цього отримують багато користі.
CodexArcanum

1

Проблема операторів полягає в тому, що їх існує лише невелика кількість порівняно з кількістю чутливих (!) Імен методів, які можна використовувати замість цього. Побічним ефектом є те, що визначені користувачем оператори, як правило, вводять багато перевантажень.

Звичайно, рядок C = A * x + b * cпростіше писати і читати, ніж C = A.multiplyVector(x).addVector(b.multiplyScalar(c)).

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

Тепер для більшості кодів, які там будуть працювати, це добре. "Проект проходить усі тести, і він працює" - для багатьох програмне забезпечення, це все, що ми могли хотіти.

Але все виходить інакше, коли ви працюєте з програмним забезпеченням, яке переходить у критичні місця. Критичні питання безпеки. Безпека. Програмне забезпечення, яке підтримує літаки в повітрі або підтримує роботу ядерних установок. Або програмне забезпечення, яке шифрує ваші дуже конфіденційні листи.

Для експертів з безпеки, які спеціалізуються на аудиторському коді, це може бути кошмаром. Сукупність безлічі визначених користувачем операторів і перевантаження операторів може поставити їх у неприємну ситуацію, що їм важко з'ясувати, який код буде працювати в кінцевому підсумку.

Отже, як сказано в первісному питанні, це дуже суб’єктивний предмет. І хоча багато пропозицій про те, як використовувати операторів, можуть здаватися абсолютно чуйними, поєднання лише декількох з них може створити багато проблем у довгостроковій перспективі.


0

Я вважаю, що самий крайній приклад - APL, наступна програма - "гра життя":

life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}

І якщо ви зможете розібратися, що все це означає, не витрачаючи пару днів на довідник, тоді удачі вам!

Проблема полягає в тому, що у вас є набір символів, який майже всі інтуїтивно розуміють: +,-,*,/,%,=,==,&

і решта, які потребують певного пояснення, перш ніж їх можна зрозуміти, і, як правило, характерні для конкретної мови. Наприклад, немає явного символу, який би вказував, який член масиву ви хочете, [], () і навіть ". були використані, але в рівній мірі немає очевидного ключового слова, яке ви могли б легко використати, тому є необхідний випадок для розумного використання операторів. Але їх не надто багато.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.