Почему языки требуют скобок вокруг выражений, когда они используются с «if» и «while»?

67

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

if (true) {
    // Do something
}

в отличие от

if true {
    // Do something
}

Это кажется мне странным, потому что круглые скобки излишни. В этом примере trueэто отдельное выражение само по себе. Скобки не меняют его значение никоим образом, я знаю. Почему существует этот странный синтаксис и почему он так распространен? Есть ли польза от этого, о которой я не знаю?

Velovix
источник
20
Паскаль не требует скобок (потому что это требует THEN).
JimmyB
30
Python, Ruby нет.
smci
31
Я считаю, что C использует круглые скобки, потому что фигурные скобки являются необязательными для тела с одним утверждением. Или, возможно, лучший способ сказать, что фигурные скобки не являются частью ifутверждения, они просто создают составное утверждение.
Фред Ларсон
7
Перейти, что интересно, требует скобки, но не скобки.
Кос
25
Вопрос немного тавтологический. Почему круглые крышки люков круглые? Почему все братья мужчины? Почему все языки, требующие паренов, нуждаются в паренсе? Круглые крышки люков круглые по определению; братья - мужчины по определению; языки, требующие использования паренов, по определению требуют паренсов.
Эрик Липперт

Ответы:

155

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

В некоторых языках нет условными вообще , например , в Smalltalk, Self, новояз, Ио, 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, где вычисленное значение должно быть либо присвоено переменной, либо передано какому-либо другому оператору), либо где нет префиксных операторов, вы всегда должны иметь возможность в любом случае определить конец выражения и начало оператора. Я определенно мог видеть вариант BASIC, управляющий без разделителя в конце оператора IF.
Периата Breatta
4
Кроме того, поскольку C был разработан в 70-х годах, а вычисления были дорогостоящими, добавление небольшого количества скобок, вероятно, сделало бы анализатор немного легче для написания.
Мачадо
4
Re: Lisp: "собственно, макросы". На практике IF - это специальная форма в Scheme и CL (только для полноты).
coredump
1
@Leushenko: И, например, в MISC, который является ленивым по умолчанию, все условные формы являются просто обычными функциями, ни макросами, ни специальными формами. (Действительно, AFAIR, MISC имеет нулевые специальные формы?)
Йорг W Mittag
70

Скобки не нужны, только если вы используете скобки.

if true ++ x;

Например становится двусмысленным без них.

Telastyn
источник
28
@RobertHarvey - Скобки которые требуют почти каждый язык я знаю. Конечно, С и его родственник. OP спрашивает , почему они являются необходимы - и это потому , что язык станет неоднозначным иначе.
Теластин
25
Напротив, скобки не требуются для if: базового, ассемблера, python, bash / zsh, tcl, batch, brainfuck или машинного кода. Отсутствие скобок делает ifдвусмысленным, только если язык был разработан, чтобы зависеть от них.
candied_orange
12
Я удивлен, что никто не упомянул самую логичную и читаемую версию - в Паскале (включая Delphi) это так if Condition then ....
Ульрих Герхардт
18
Go - хороший пример того, как поступить наоборот. Это делает скобки {}обязательными и, следовательно, не требует скобок вокруг выражения. Мало того, что парены не требуются, но если я правильно помню, добавление паренов может привести к ошибке компиляции - они запрещены
slebetman
10
@Eiko Позволь мне перефразировать. Пример в ответе синтаксически неоднозначен, хотя семантически однозначен (как вы заметили). Но так как фаза синтаксического анализа происходит до семантического анализа, синтаксический анализатор столкнется с неоднозначностью - и он должен либо сделать неосведомленное предположение, либо потерпеть неудачу. Если (по какой-либо причине) синтаксический анализатор решит не сбоить, семантический анализатор будет работать с результирующим деревом, что бы это ни было. Я не видел компилятора, в котором семантический анализатор хочет попросить синтаксический анализатор повторно проанализировать поддерево и сделать другой выбор в синтаксически неоднозначной конструкции.
Теодорос Чатзигианнакис
21

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

В ifоператоре круглые скобки не выполняют функцию группировки (хотя внутри ifинструкции вы все равно можете использовать круглые скобки для группировки арифметических выражений. Внешний набор скобок затем служит для разграничения всего логического выражения). Их обязательное выполнение упрощает компилятор, поскольку компилятор может полагаться на те круглые скобки, которые всегда присутствуют.

Роберт Харви
источник
Я не вижу, как это упрощает компилятор. Правило `IF '(' expression ')' Statement` не проще, чем IF primary_expression statement. Обратите внимание, что последнее одинаково однозначно.
user58697
@ user58697: Нет, только у последнего есть неоднозначность, из-за которой постфиксный оператор primary_expressionнельзя отличить от префиксного оператора в выражении-выражении. Чтобы скопировать ответ Теластина if true ++ x;. Кроме того, если существуют пустые операторы, они if a & f;могут быть либо пустым оператором и двоичным кодом &внутри условия, либо унарным &в начале оператора. Но при сопоставлении скобок для открытия есть ровно одно совпадение (
MSalters
@MSalters Постфиксный оператор не анализируется как основной. Первичное выражение является одним из IDENTIFIER, CONSTANT, STRING_LITERALи '(' expression ')'.
user58697
@ user58697: Вы, кажется, имеете в виду определенный язык. И, похоже, есть правило, что круглые скобки не нужны, если и только если условие является «IDENTIFIER, CONSTANT или STRING_LITERAL». Я не уверен, что это облегчает ситуацию.
MSalters
16

Как другие уже частично указали, это связано с тем, что выражения также являются допустимыми операторами, и в случае блока с одним оператором вы можете удалить фигурные скобки. Это означает, что следующее неоднозначно:

if true
    +x;

Потому что это можно интерпретировать как:

if (true + x) {}

вместо:

if (true) {+x;}

Ряд языков (например, Python) позволяют вам избегать скобок, но все же имеют маркер конечного условия:

если True : + 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.

Bakuriu
источник
2
На самом деле существует язык, который использует такой маркер выражения выражения: если вы хотите оценить выражение на предмет его побочных эффектов, вам необходимо явно указать discardего значение в Nim . Однако это сделано только для безопасности типов, а не по синтаксическим причинам.
Амон
@amon Хорошо, я не знал об этом. В любом случае, как я уже сказал, маркер на самом деле не нужен, это просто простой способ добиться этого различия без изобретения неинтуитивных синтаксисов.
Бакуриу
1
@amon - многие варианты BASIC также имеют строгое разделение между выражениями и утверждениями. Выражения разрешены только в тех местах, где значение будет фактически использоваться (например, присваивание переменных, операторы типа PRINT, выполняющие действие и т. Д.). Процедуры, которые не используются для вычисления значения, вызываются ключевым словом (обычно «CALL», хотя, по крайней мере, один известный мне вариант использует «PROC»), которое ставит префикс их имени. И так далее. BASIC обычно разделяет конец выражения в выражении IF с помощью «THEN», но я не вижу технической причины, по которой требование не может быть отброшено.
Периата Breatta
1
В 80-х годах появился очень популярный язык, в котором есть блоки без скобок, скобок, двоеточий или другого маркера, и они принимают выражения как операторы везде, а некоторые операторы действуют как выражения (составные операторы, такие как + = и ++). Хуже того, перед компилятором стоит тупой препроцессор ( ?например, simbol - это функция после PP). Существует нет ;. Конечно, нужен маркер для продолжения линии, но это не рекомендуется. harbour.github.io/doc/clc53.html#if-cmd . Компилятор быстрый и простой (создан с помощью Bison / Flex).
Maniero
@bigown Они добиваются , что с помощью отдельного синтаксиса для логического условия, поэтому, в основном, условия if, whileЕСС ограничены по сравнению с родовыми выражений , используемых в других языках. Конечно: если у вас есть более двух синтаксических категорий (например, выражение, выражение, логическое выражение, выражение для приготовления кофе, ...), вы можете торговать некоторой свободой.
Бакуриу
10

Для языков семейства C характерны такие скобки, но они не универсальны.

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

if $x == 4 {
    ...
}

как есть

while $queue.pop {
    ...
}

Однако, поскольку они являются просто выражениями, вы можете заключить их в круглые скобки, если хотите, и в этом случае они являются просто обычными группирующими вместо требуемой части синтаксиса, как в C, C #, Java и т. Д.

В Rust синтаксис похож на Perl 6 в этом отделе:

if x == 4 {
    ...
}

Мне кажется, что особенность более современных языков, основанных на C, состоит в том, чтобы смотреть на подобные вещи и удивляться их удалению.

Мэтью Уолтон
источник
Хотя ваш ответ дает некоторое представление о других языках, он не отвечает «Почему существует этот странный синтаксис и почему он так распространен?»
Вы совершенно правы. Я действительно хотел добавить контекст к вопросу, что нелегко сделать на этой платформе. В конечном счете, я полагаю, что если я даю ответ на вопрос, то это «просто потому, что по техническим причинам грамматика не смогла бы справиться с их отсутствием». Но это не очень полезный ответ.
Мэтью Уолтон
В Perl 5 есть и то и другое. Для нормальных if или циклических конструкций с БЛОКАМИ требуются скобки, например, в 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 рассматривает ненулевое значение как true; кстати, я думаю, что это просто strncpy () в стандартной библиотеке C)

или даже

if (x = 17);

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

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

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

Конечно, альтернативные синтаксисы могут быть определены, чтобы сделать то же самое. Но это увеличило бы сложность, особенно в синтаксическом анализаторе, который затем должен был бы иметь дело с двумя различными наборами синтаксиса для почти одной и той же вещи. В то время, когда разрабатывался C, вычислительная мощность (как с точки зрения способности к сокращению чисел, рабочей памяти и емкости памяти) была крайне ограничена; все, что уменьшало сложность при минимальных затратах или вообще не приводило к читабельности, почти наверняка было долгожданным изменением.

Использование скобок сегодня может показаться немного архаичным, но это не так, если кто-то знаком с языком, это ухудшает читабельность по сравнению с другим синтаксисом, способным выражать те же вещи.

CVn
источник
5

Причина в основном история.

В то время, когда был написан первый компилятор C, на компьютерах были очень ограничены ram, cpu и компиляторы, где они были написаны «вручную» с несколькими инструментами, помогающими разработчикам компиляторов. Поэтому сложные правила были дорогостоящими для реализации в компиляторе. C ++, C #, Java и т. Д. Были разработаны так, чтобы программистам C было легко их освоить, поэтому «ненужных» изменений не было.

В языках «c like» условные выражения ( if, while, etc) не требуют явного blockотключения кода, вы можете просто использовать простое утверждение.

if (a == d) doIt()

или вы можете объединить заявления в один compound statement, поместив их в{}

Нам нравится, когда компилятор находит ошибку, которую мы совершаем, и выдает сообщение об ошибке, которое мы можем понять.

Ян
источник
3

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

Следовательно, причина того, почему все эти языки требуют скобок вокруг условий этих утверждений, заключается в том, что C делает, и вопрос в том, почему C требует эти скобки.

Происхождение языка Си описано в этой статье Деннисом Ритчи, одним из главных авторов его разработки (некоторые могут даже сказать, что основным автором его разработки). Как сказано в этой статье, C был изначально разработан в начале 1970-х годов как язык системного программирования для компьютеров с чрезвычайно ограниченным пространством в основной памяти. Было желательно иметь язык более высокого уровня, чем ассемблер, но, учитывая ресурсы, доступные для работы, простота синтаксического анализа языка также была важна. Требование скобок позволит относительно легко идентифицировать условный код.

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

В любом случае необходимо было найти некоторый баланс между тем, насколько легко люди смогут читать, писать и понимать программы, написанные на C, как легко компилятор может анализировать и компилировать программы, написанные на C, и сколько килобайт (!) потребуется как для исходного кода программы, так и для самого компилятора. И круглые скобки вокруг условий if, whileи switch заявлений было то, как люди решили ударить , что баланс в конструкции С.

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

Дэвид К
источник
Я не уверен, что будет справедливо сказать, что C ++ был спроектирован так, как это было «для того, чтобы заставить этих программистов использовать новый язык». Помните C с классами ?
CVN
@ MichaelKjörling По общему признанию, разработчики Java были намного более явными о "ухаживании". Но обратите внимание, что в связанной статье в качестве одной из причин, по которой Страуструп решил начать с C в качестве основы своего языка, указывается, что C широко использовался. Одним из способов, которым это послужило мотивацией оставаться рядом с C, было то, что существующий код можно было легко адаптировать (как я уже говорил), но также можно легко адаптировать существующие кодеры .
Дэвид К
@ MichaelKjörling Я предполагаю, что первоначальная формулировка моего ответа предполагала, что «ухаживание» было более значительным фактором в дизайне языка, чем на самом деле. Я отредактировал ответ, чтобы попытаться уточнить, что это просто одна вещь, которая учитывается при проектировании языка.
Дэвид К
3

Многие здесь считают, что без скобок синтаксис был бы неоднозначным и молчаливо подразумевали, что это будет как-то плохо или даже невозможно.

На самом деле, у языков есть много способов справиться с неясностями. Приоритет оператора - только один из примеров этой темы.

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

Вопрос о том, почему Деннис Ритчи решил сделать () обязательным (и, следовательно, придумать этот мем для большого количества производных языков), довольно лингвистический. Я предполагаю, что идея ясно заявить, что условие является выражением, а не командой, была отцом мысли.

И на самом деле, C был разработан для анализа с использованием однопроходного синтаксического анализатора. Использование синтаксиса с обязательными круглыми скобками вокруг условия поддерживает этот аспект.

Альфей
источник
Я вижу отрицательный ответ на мой ответ. Пожалуйста, будьте любезны объяснить в комментарии, что вам не понравилось в этом. Может быть, я могу улучшить это тогда.
Alfe
0

Скобки вокруг ifусловий не требуются в Фортране, Коболе, PL / 1, Алголе, Алго-68, Паскале, Модуле, XPL, PL / M, MPL, ... или любом другом языке, имеющем thenключевое слово. thenслужит для отделения conditionот следующего statement.

Закрывающая скобка в C и т. Д. Функционирует как then, а открывающая скобка формально избыточна.

Приведенные выше замечания относятся к традиционно анализируемым языкам.

user207421
источник
Fortran делает требует скобок во всех версиях его IF, в том числе структурным.
Нетч