Чому для мов потрібні дужки навколо виразів, коли вони використовуються з "якщо" та "поки"?


67

Мови , як C, Java і C ++ все вимагають дужки навколо усього виразу при використанні в if, whileабо switch.

if (true) {
    // Do something
}

на відміну від

if true {
    // Do something
}

Мені це здається дивним, оскільки дужки є зайвими. У цьому прикладі trueє окремим виразом самостійно. Дужки не перетворюють його значення жодним чином я знаю. Чому існує цей дивний синтаксис і чому він такий поширений? Чи є користь для цього, про який я не знаю?


20
Pascal не потребує дужок (тому що він вимагає a THEN).
JimmyB

30
Пітон, Рубі ні.
smci

31
Я вважаю, що C використовує круглі дужки, оскільки дужки необов’язкові для тіла з одним оператором. Або, можливо, кращим способом є те, що дужки не є частиною ifзаяви, вони просто створюють складене твердження.
Фред Ларсон

7
Цікаво, що потрібні дужки, але не дужки.
Кос

25
Питання трохи тавтологічне. Чому кришки люка круглі? Чому всі брати чоловіки? Чому для всіх мов, які вимагають батьків, потрібні паролі? Круглі кришки люків круглі за визначенням; брати чоловіки за визначенням; мови, які потребують паронів, вимагають паронів за визначенням.
Ерік Ліпперт

Відповіді:


155

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

У деяких мовах умовні умови взагалі відсутні , наприклад, у Smalltalk, Self, Newspeak, Io, Ioke, Seph та Fancy. Умовне розгалуження просто реалізується як звичайний метод, як і будь-який інший метод. Метод реалізується на булевих об'єктах і називається булевим. Таким чином, умова є просто приймачем методу, і дві гілки є двома аргументами, наприклад, у Smalltalk:

aBooleanExpression ifTrue: [23] ifFalse: [42].

У випадку, якщо ви більше знайомі з Java, це еквівалентно наступному:

aBooleanExpression.ifThenElse(() -> 23, () -> 42);

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

(if aBooleanExpression 23 42)

Деякі мови використовують ключові слова як роздільники, наприклад, Algol, Ada, BASIC, Pascal, Modula-2, Oberon, Oberon-2, Active Oberon, Component Pascal, Zonnon, Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

У Ruby ви можете використовувати або ключове слово, або роздільник виразів (крапка з комою або новий рядок):

if a_boolean_expression then 23 else 42 end

if a_boolean_expression; 23 else 42 end

# non-idiomatic, the minimum amount of whitespace required syntactically
if a_boolean_expression
23 else 42 end

# idiomatic, although only the first newline is required syntactically
if a_boolean_expression
  23
else
  42
end

Go вимагає, щоб гілки були блоками і не дозволяли вирази або заяви, що робить фігурні дужки обов'язковими. Тому дужки не потрібні, хоча ви можете додати їх, якщо хочете; У цьому відношенні Perl6 і Rust схожі:

if aBooleanExpression { return 23 } else { return 42 }

Деякі мови використовують інші не алфавітно-цифрові символи для розмежування умови, наприклад, Python:

if aBooleanExpression: return 23
else: return 42

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


8
Дуже хороший огляд.
Пітер А. Шнайдер

2
звичайно мовою, де або голі вирази не є твердженням (наприклад, щось на зразок старшого BASIC, де обчислене значення повинно бути присвоєно змінній, або передане іншому оператору) або де немає операторів префікса, ви завжди повинні мати можливість щоб в будь-якому разі визначити кінець виразу та початок висловлювання. Я точно міг бачити базовий варіант управління без роздільника в кінці оператора IF.
Periata Breatta

4
Крім того, оскільки C був спроектований у 70-х, а обчислення були дорогими, додавання трохи дужок, можливо, полегшило б парсер для написання.
Мачадо

4
Re: Lisp: "власне, макроси". На практиці ІФ є спеціальною формою в Scheme та CL (тільки для повноти).
coredump

1
@ Левшенко: І, наприклад, у MISC, який за замовчуванням лінивий, усі умовні форми - це просто нормальні функції, ні макроси, ні спеціальні форми. (Дійсно, AFAIR, MISC має нульові спеціальні форми?)
Jörg W Mittag

70

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

if true ++ x;

Наприклад, стає неоднозначним без них.


28
@RobertHarvey - Дужки які вимагають майже кожна мова я знаю. Безумовно, С та його спорідненість. OP запитує , чому вони є необхідні - і це тому , що мова стане неоднозначним інакше.
Теластин

25
У верхній частині моєї дужки не потрібні дужки ifв: basic, Assembly, python, bash / zsh, tcl, batch, brainfuck або машинний код. Відсутність дужок стає ifдвозначним лише тоді, коли мова була розроблена так, щоб залежати від них.
candied_orange

12
Я здивований, що ніхто не згадував про найбільш логічну і читабельну версію - у Pascal (включаючи Delphi) це if Condition then ....
Ульріх Герхардт

18
Go - хороший приклад робити протилежне. Це робить брекети {}обов'язковими і тому не вимагає паронів навколо виразу. Мало того, що парен не потрібно, але якщо я пам’ятаю, що правильно додавати паролі, це призведе до помилки компіляції - вони заборонені
slebetman

10
@Eiko Дозвольте перефразувати. Приклад у відповіді синтаксично неоднозначний, навіть незважаючи на те, що він семантично однозначний (як ви зазначали). Але оскільки фаза розбору відбувається перед семантичним аналізом, аналізатор зіткнеться з неоднозначністю - і він повинен або зробити неінформовану здогадку, або зазнати невдачі. Якщо (з будь-якої причини) аналізатор вирішить не вийти з ладу, семантичний аналізатор буде працювати з отриманим деревом, що б це не було. Я не бачив компілятора, в якому семантичний аналізатор готовий попросити аналізатор переаналізувати підрівень і зробити інший вибір у синтаксично неоднозначній конструкції.
Теодорос Чатзіґянакікіс

21

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

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


Я не бачу, як це спрощує компілятор. Правило `IF '(' вираз ')' твердження 'не простіше, ніж IF primary_expression statement. Зауважте, що остання однаково однозначно.
користувач58697

@ user58697: Ні, лише останній має неоднозначність, коли оператор постфіксу на primary_expressionне може бути відрізнений від оператора префікса в операторі вираз-оператор. Для того, щоб скопіювати відповідь Telastyn, в if true ++ x;. Крім того, якщо порожні оператори існують, вони if a & f;можуть бути або порожнім оператором, і двійковим &усередині умови, або унарним &на початку оператора. Але при узгодженні дужок є рівно одна відповідність для відкриття (
MSalters

@MSalters Постфіксний оператора робить НЕ синтаксичний аналіз в якості первинного. Первинне вираз є одним з IDENTIFIER, CONSTANT, STRING_LITERALі '(' expression ')'.
користувач58697

@ user58697: Ви, мабуть, маєте на увазі певну мову. І, мабуть, існує правило, що дужки не потрібні, якщо і лише якщо умови "IDENTIFIER, CONSTANT або STRING_LITERAL". Я не впевнений, що це полегшує справи.
MSalters

16

Як інші вже частково вказали, це пов'язано з тим, що вирази також є дійсними твердженнями, а у випадку з блоком, що містить лише один вислів, ви можете скидати дужки. Це означає, що наступне неоднозначне:

if true
    +x;

Тому що це можна трактувати як:

if (true + x) {}

замість:

if (true) {+x;}

Кілька мов (наприклад, Python) дозволяють уникнути дужок, але все ще мають маркер кінцевих умов:

якщо вірно : + x

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

На жаль, це означає, що такі речі, як:

 ++x;
 functionCall(1,2,3);

б НЕ бути дійсним заявою, так що ви повинні були б ввести деякі дивний синтаксис , щоб мати можливість виконувати такі дії , не створюючи вираження. Простий спосіб зробити це - просто додавати вираз маркером, як [statement]:

[statement] ++x;
[statement] functionCall(1,2,3);

Тепер двозначність зникає, оскільки вам доведеться писати:

if true
    [statement] ++x;

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


Примітка : використання [statement]маркера - це просто найпростіший синтаксис, про який я міг придумати. Однак у вас можуть бути два абсолютно різних синтаксису для виразів і висловлювань, без яких-небудь неоднозначностей, що не вимагало б такого маркера. Проблема полягає в тому, що мова буде надзвичайно дивною, оскільки для того, щоб робити те саме в виразі чи вислові, вам доведеться використовувати зовсім інший синтаксис.

Одна речі , яка приходить на розум , щоб мати два окремі синтаксису без таких явних маркерів буде, наприклад: заяви змушують використовувати юнікод символи (так що замість forвас би використовувати деякі юнікод зміна букв f, oа r), в той час як вираження , щоб бути Тільки ASCII.


2
Насправді існує мова, яка використовує такий маркер висловлення виразів: Якщо ви хочете оцінити вираз за його побічними ефектами, вам потрібно чітко вказати discardйого значення в Nim . Однак це робиться тільки для безпеки типу, а не з синтаксичних причин.
амон

@amon Ніцца, я про це не знав. У будь-якому випадку, як я сказав, маркер насправді не потрібен, це просто простий спосіб досягти цього розрізнення, не вигадуючи інтуїтивні синтаксиси.
Бакуріу

1
@amon - багато варіантів BASIC також мають суворий поділ між виразами та твердженнями. Вирази дозволені лише в тих місцях, де значення буде фактично використане (наприклад, присвоєння змінних, оператори типу PRINT, які виконують дію тощо). Процедури, які не використовуються для обчислення значення, викликаються ключовим словом (як правило, "CALL", хоча принаймні один варіант, який я знаю, використовує "PROC"), що префіксує їх ім'я. І так далі. BASIC зазвичай розмежовує кінець виразу в операторі IF з "THEN", але я не бачу жодної технічної причини, щоб цю вимогу не можна було скинути.
Periata Breatta

1
Існує дуже популярна мова у 80-х роках, яка містить блоки без паролів, дужок, колонок чи іншого маркера і приймає вирази як твердження скрізь, а деякі твердження діють як вирази (складні оператори, такі як + = і ++). Це борт, перед компілятором є німий попередній процесор ( ?символ, наприклад, є функцією після PP). Немає ;. Звичайно, йому потрібен маркер для лінії продовження, але це не рекомендується. harbour.github.io/doc/clc53.html#if-cmd . Компілятор швидкий і простий (створений за допомогою Bison / Flex).
Маньєро

@bigown Вони досягають цього, використовуючи окремий синтаксис для логічних умов, тому, в основному, умови для if, whileecc обмежені порівняно із загальними виразами, які використовуються в інших мовах. Впевнено: якщо у вас є більше двох синтаксичних категорій (наприклад, висловлювання, вираз, логічне вираження, кавовий вираз, ...), ви можете торгувати деякою свободою.
Бакуріу

10

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

Одним з найбільш помітних синтаксичних змін Perl 6 є те , що вони змінили граматику так , що ви не повинні давати круглі дужки if, forі умов аналогічних заяв. Тож щось подібне ідеально діє в Perl 6:

if $x == 4 {
    ...
}

як є

while $queue.pop {
    ...
}

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

У Іржі синтаксис схожий на Perl 6 у цьому відділі:

if x == 4 {
    ...
}

Мені здається, що особливістю сучасних мов, натхнених С, - це дивитись на такі речі і дивуватися їх видаленню.


Хоча ваша відповідь дає деяке розуміння інших мов, вона не відповідає "Чому існує цей непарний синтаксис і чому він такий поширений?"

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

У Perl 5 є і те, і інше. Для нормальних if або контурних конструкцій з BLOCKs необхідні паролі, наприклад, в if ( $x == 4 ) { ... }або foreach my $foo ( @bar ) { ... }. Якщо використовується позначення постфіксу, паролі необов'язкові, як у return unless $foo;або ++$x while s/foo/bar/g;.
simbabque

6

Є один аспект, який я дивуюсь, що жоден із існуючих відповідей не підніс.

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

Це дозволяє писати такі речі

if (x = getValue() == 42) { ... }

або

if (x == y = 47) { ... }

або

unsigned int n = 0 /* given m == SOME_VALUE */;
while (n < m && *p1++ = *p2++) { n++; }

(що неявно трактується так, while (n < m && *p1++ = *p2++ != 0) { n++; }тому що C трактує ненульовий стан як істинний; до речі, я думаю, що мова йде лише про strncpy () у стандартній бібліотеці C)

або навіть

if (x = 17);

і все це дійсно. Не всі синтаксично допустимі комбінації обов'язково корисні (а сучасні компілятори спеціально попереджають про призначення всередині умовних умов, оскільки це звичайна помилка), але деякі з них насправді корисні.

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

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

Зрозуміло, альтернативні синтаксиси можна визначити, щоб зробити те саме. Але це зробить складність, особливо в синтаксичному аналізі, який тоді повинен мати справу з двома різними наборами синтаксису в основному одне і те ж. Ще коли розроблявся C, обчислювальна потужність (як з точки зору здатності до скорочення чисельності, робочої пам’яті, так і ємності зберігання) була надзвичайно обмежена; все, що зменшило складність практично або зовсім не зручно для читабельності, майже напевно було бажаною зміною.

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


5

Причина - здебільшого історія.

На той момент, коли був написаний перший компілятор C, комп'ютери мали дуже обмежений об'єм оперативної пам’яті, процесора та компіляторів, де писали «вручну» з нечисленними інструментами, які допомагають авторам-компіляторам. Тому складні правила було дорого реалізувати в компіляторі. C ++, C #, Java та ін. Були розроблені так, щоб програмістам на C було легко навчатись, отже, не було внесено "зайвих" змін.

У мовах 'c like' conditionals ( if, while, etc) не потребують явного blockкоду вимкнення, ви можете просто скористатися простим оператором.

if (a == d) doIt()

або ви можете об'єднати висловлювання разом у compound statement, додавши їх у{}

Нам подобається, що компілятор знаходив помилку, яку ми робимо, і подавати як повідомлення про помилку, яке ми можемо зрозуміти.


3

І Java, і C ++ були розроблені після того, як C став дуже популярною мовою програмування. Одне з питань розробки кожної з цих мов полягало в тому, що воно звернеться до програмістів на C і змусить цих програмістів використовувати нову мову. (Я був одним із програмістів на C, з якими вони успішно виступили.) Крім того, C ++ був розроблений таким чином, щоб бути (майже) взаємозамінним з кодом C. Для підтримки цих цілей, як C ++ і Java взяли більшу частину синтаксису мови C, в тому числі круглих дужок навколо умов if, whileі switchзаяви.

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

Витоки мови C описані в цій статті Денніс Рітчі, один з головних авторів його розвитку (деякі з них можуть навіть сказати , на головний автор його розвитку). Як сказано в цій статті, C спочатку був розроблений на початку 1970-х років як система системного мови програмування для комп'ютерів з надзвичайно обмеженим простором основної пам'яті. Хотілося, щоб мова була вищою, ніж мова монтажу, але враховуючи доступні ресурси для роботи, важлива була і легкість розбору мови. Потрібна дужка зробить відносно легким ідентифікацію умовного коду.

Можна також зробити висновок, що можливість запису програм з використанням меншої кількості символів вважалася перевагою, і дві дужки займають менше місця, ніж ключове слово, THENяке використовувалося в FORTRAN та інших мовах високого рівня в той час; насправді, оскільки дужки також могли замінити пробіли як роздільники символів, на if(a==b)чотири цілих символи було коротше IF a==b THEN.

У будь-якому випадку потрібно досягти балансу між тим, наскільки легко люди зможуть читати, писати та розуміти програми, написані на С, наскільки легко компілятор може розбирати та компілювати програми, написані на С, та скільки кілобайт (!) буде потрібно як для джерела програми, так і для самого компілятора. І круглі дужки навколо умов if, whileі switch заяв було те, як люди вирішили вдарити , що баланс в конструкції С.

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


Я не впевнений, що справедливо сказати, що C ++ був розроблений так, як це було "для того, щоб змусити цих програмістів використовувати нову мову". Пам'ятаєте C з класами ?
CVn

@ MichaelKjörling Справді, розробники Java були набагато чіткішими щодо "сватання". Але зауважте, що пов'язана стаття наводить, як одну з причин, чому Струструпп вирішив почати з C як основи своєї мови, що С широко використовувався. Один із способів, за допомогою якого це забезпечувало мотивацію залишатися поруч із C, - це тому, що існуючий код можна було легко адаптувати (як я вже заявив), - але також існуючі кодери могли легко адаптуватися.
Девід К

@ MichaelKjörling Я вважаю, що в оригінальній формулярі моєї відповіді було висловлено припущення, що «сватання» - це більший фактор мовного дизайну, ніж насправді. Я відредагував відповідь, щоб спробувати уточнити , що в мовному дизайні було враховано лише одне .
Девід К

3

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

Насправді в мовах є безліч способів вирішити неясності. Пріоритет оператора - це лише один екземпляр цієї теми.

Ні, неоднозначність не є причиною для дужок. Я здогадуюсь, можна просто створити версію C, яка не потребує дужок навколо умови (тим самим робить їх необов'язковими) і яка все ще створює дійсний код у всіх випадках. Приклад цього if a ++ b;можна інтерпретувати як еквівалентний if (a) ++b;або if (a++) b;, що здається, більш підходящим.

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

Фактично, C був розроблений як синтаксичний з використанням однопрохідного аналізатора. Використання синтаксису з обов'язковими дужками навколо умови підтримує цей аспект.


Я бачу сутичку на свою відповідь. Будь ласка, будь ласка, поясніть у коментарі те, що вам не сподобалось у цьому. Можливо, я можу тоді її вдосконалити.
Алфе

0

Дужки навколо ifумов не потрібні у Fortran, Cobol, PL / 1, Algol, Algo-68, Pascal, Modula, XPL, PL / M, MPL, ... або будь-якій іншій мові, яка має thenключове слово. thenслужить для відмежування conditionвід наступного statement.

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

Наведені вище зауваження стосуються традиційно розбірних мов.


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