Чому для кожного є двокрапка замість "в"?


9

З мовного посібника Java 5 :

Коли ви побачите товсту кишку (:) прочитайте її як "в".

Чому б тоді не використовувати inв першу чергу?

Це клопоче мене роками. Тому що це не відповідає решті мови. Наприклад, в Java є implements, extends, superдля відносин між типами замість символів , як в C ++, Scala або Ruby.

У Java двокрапка використовується в 5 контекстах . Три з них успадковані від C. А інших двох було схвалено Джошуа Блохом. Принаймні, про це він говорив під час розмови про "закриття суперечки" . Це з'являється, коли він критикує використання двокрапки для відображення як невідповідну для кожної семантики. Що мені здається дивним, оскільки це для кожного зловживаних очікуваних моделей. Як list_name/category: elementsі laberl/term: meaning.

Я проліз навколо jcp та jsr, але не знайшов жодної ознаки списку розсилки. Google не знайшов дискусій з цього приводу. Тільки новачки плутають значення двокрапки в for.


Основні аргументи проти inнаданих до цього часу:

  • вимагає нового ключового слова; і
  • ускладнює лексинг.

Давайте розглянемо відповідні граматичні визначення:

заява
    : оператор 'for' '(' forControl ')'
    | ...
    ;

дляControl
    : покращенийForControl
    | дляІніт? ';' вираз? ';' forUpdate?
    ;

розширенийForControl
    : зміннийModifier * тип змінDeclaratorId ':' вираз
    ;

Перехід від :до inНЕ принести додаткову складність або вимагає нового ключового слова.


1
Найкращим джерелом для з’ясування мотивацій мовних дизайнерів часто є самі дизайнери. Однак, це, мабуть, просто синтаксичний цукор над ітерабельним; див. stackoverflow.com/questions/11216994/…
Роберт Харві

Відповіді:


8

Нормальні парсери, як їх зазвичай навчають, мають лексерну стадію, перш ніж аналізатор торкнеться введення. Лексер (також "сканер" або "токенізатор") розбиває вхід на невеликі лексеми, які позначаються типом. Це дозволяє основному аналізатору використовувати лексеми як термінальні елементи, а не потребувати трактування кожного символу як терміналу, що призводить до помітного підвищення ефективності. Зокрема, лексер також може видалити всі коментарі та пробіл. Однак окрема фаза токенізатора означає, що ключові слова також не можуть бути використані як ідентифікатори (якщо мова не підтримує перехід, який дещо не прихильний, або префіксує всі ідентифікатори подібними до сигілів $foo).

Чому? Припустимо, у нас є простий токенізатор, який розуміє такі лексеми:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

Токенізатор завжди буде відповідати найдовшому маркеру і віддасть перевагу ключовим словам над ідентифікаторами. Так interestingбуде lexed , як IDENT:interesting, але inНЕ буде lexed , як IN, буває так IDENT:interesting. Фрагмент коду, як

for(var in expression)

буде переведено в потік токенів

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

Поки що це працює. Але будь-яка змінна inбуде використана як ключове слово, INа не змінна, яка б порушила код. Лексер не зберігає жодного стану між маркерами, і не може знати, що inзазвичай має бути змінною, за винятком випадків, коли ми знаходимося в циклі for. Також наступний код повинен бути легальним:

for(in in expression)

Перший inбув би ідентифікатором, другий - ключовим словом.

Є дві реакції на цю проблему:

Контекстуальні ключові слова заплутані, давайте повторно використовувати ключові слова.

У Java є багато зарезервованих слів, деякі з яких не мають жодного користі, крім надання корисніших повідомлень про помилки програмістам, які переходять на Java з C ++. Додавання нових ключових слів код перерви. Додавання контекстних ключових слів заплутає читача коду, якщо вони не мають гарного підкреслення синтаксису, і ускладнює втілення інструментів, тому що їм доведеться використовувати більш досконалі методики розбору (див. Нижче).

Коли ми хочемо розширити мову, єдиним розумним підходом є використання символів, які раніше не були легальними для мови. Зокрема, це не можуть бути ідентифікатори. За допомогою синтаксису циклу foreach Java повторно використала існуюче :ключове слово з новим значенням. За допомогою лямбда, Java додала ->ключове слово, яке раніше не могло виникнути в жодній юридичній програмі (як -->і раніше воно буде лексиковане, як '--' '>'законне, і ->раніше воно могло б бути використане як ліцензія '-', '>', але цю послідовність буде відхилено парсером).

Контекстуальні ключові слова спрощують мови, давайте їх реалізувати

Лексери безперечно корисні. Але замість запуску лексеру перед парсером, ми можемо запустити їх у тандемі з парсером. Розбіжники знизу вгору завжди знають набір типів токенів, який був би прийнятний у будь-якому місці. Потім аналізатор може попросити лексеру відповідати будь-якому з цих типів у поточній позиції. У циклі для кожного циклу аналізатор буде знаходитись у положенні, позначеному ·в (спрощеній) граматиці після того, як змінна знайдена:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

У цій позиції, юридичні лексеми SEMICOLONабо IN, але не IDENT. Ключове слово inбуло б абсолютно однозначним.

У цьому конкретному прикладі аналізатори зверху вниз також не матимуть проблем, оскільки ми можемо переписати вищезазначені граматики на

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

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

Розглянемо зручність використання

Java завжди прагнула до смислової та синтаксичної простоти. Наприклад, мова не підтримує перевантаження оператора, оскільки це зробить код набагато складнішим. Отже, приймаючи рішення між синтаксисом циклу inта :для кожного циклу, ми повинні враховувати, що менш заплутане та більш очевидне для користувачів. Можливо, крайній випадок

for (in in in in())
for (in in : in())

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

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


17

Синтаксис для кожного циклу доданий у Java 5. Вам потрібно буде створити inмовне ключове слово, а додавання ключових слів до мови згодом - це те, чого ви уникаєте будь-якою ціною, оскільки він порушує наявний код - раптом усі названі змінні in викликають аналіз помилка. enumбуло досить погано в цьому плані.


2
Це здається ... незручним. Це передбачає, що мовні дизайнери були досить хорошими, щоб передбачити більшість необхідних ключових слів із самого початку. Я не впевнений, що це навіть потрібно; гідні компілятори можуть визначити, чи є ключове слово змінною за його контекстом.
Роберт Харві

2
Я не думаю, що у Java є такі контекстуальні ключові слова, як у C #. Отже, використання inозначало б або ввести нове ключове слово, таким чином порушивши зворотну сумісність ( System.inхтось?), Або запровадити раніше невідому абсолютно нову концепцію (контекстні ключові слова). Все для чого виграш?
Йорг W Міттаг

2
Яка шкода контекстним ключовим словам?
користувач2418306

5
@ user2418306 Додавання ключового слова не повинно порушувати існуючий код за умови, що мова не розбирається з окремою лексеровою фазою. Зокрема, "в" for(variable in expression)ніколи не може бути неоднозначним з будь-яким юридичним кодом, навіть якщо "в" може бути використаний для змінних. Однак окрема фаза лексеру є досить частою у багатьох інструментальних компіляторах. Це зробило б неможливим або, принаймні, набагато складніше розбір Java з деякими загальними генераторами парсеру. Зберігати синтаксис мови простим, як правило, добре для всіх учасників; не всім потрібні синтаксичні жахливості, такі як C ++ або Perl.
амон

1
@RobertHarvey: Не забувайте про це constі gotoобидва зарезервовані слова на Java, але вони не використовуються (поки що).
TMN
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.