Почему точки с запятой и запятые меняются местами в циклах?

49

На многих языках (широкий список, от C до JavaScript):

  • запятые ,отдельные аргументы (например func(a, b, c)), в то время как
  • точки с запятой ;разделяют последовательные инструкции (например instruction1; instruction2; instruction3).

Так почему же это отображение в тех же языках для циклов for :

for ( init1, init2; condition; inc1, inc2 )
{
    instruction1;
    instruction2;
}

вместо (что кажется мне более естественным)

for ( init1; init2, condition, inc1; inc2 )
{
    instruction1;
    instruction2;
}

?

Конечно, forэто ( как правило) не функция, но аргументы (то есть init, condition, increment) ведут себя скорее как аргументы функции , чем последовательность инструкций.

Это связано с историческими причинами / соглашением, или есть хорошее обоснование для обмена ,и ;в циклах?

Петр Мигдаль
источник
1
(Мой первый пост здесь. Я не уверен, принадлежит ли этот вопрос больше программистам или SO, поэтому не стесняйтесь мигрировать, если это необходимо.)
Петр Мигдаль
8
Это определенно пост программистов. Добро пожаловать! :-)
Мартин Питерс
1
«Почему нет» даст ответ - имея тот же ответ - «Потому что кому-то нужно было сделать выбор, и это был выбор, который они сделали» То же, что «Почему они выбрали» {«и»} »и 1000 других вариантов выбора они сделали - «Потому что».
mattnz
2
@mattnz Вопрос касается согласованности (а не «почему мы ;не используем |?» (или почему мы используем «иначе», а не «иначе»? )), что не относится к одному языку, но к большому количеству из них. Ответ, например, «это было сделано в C как сокращение для цикла while (а несколько операторов для inc были продуманы только позже), и люди не хотели менять его, чтобы избежать раздражения программистов», был бы совершенно нормальным.
Петр Мигдаль
Я вспоминаю чтение - возможно, в K & R - что оператор запятой изначально был добавлен в язык для того, чтобы можно было инициализировать более одной переменной в выражении init оператора for.
Звол

Ответы:

18

Так почему же на тех же языках такое отображение для циклов наоборот?

Технически, отображение не «обратное».

  • Вещи, разделенные запятыми, не являются параметрами. В (по крайней мере) C ++ и Java они могут быть объявлениями, поэтому они даже не являются выражениями.
  • Вещи, разделенные точкой с запятой, также не являются (одиночными) утверждениями.

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

Так почему бы не сделать это наоборот?

Ну, я думаю, что причины происходят от "естественного" значения ,и ;. В английском письменном языке точка с запятой является «более сильным» разрывом, чем запятая, а символ точки с запятой более заметен, чем запятая. Эти две вещи объединяются, чтобы сделать текущую аранжировку (для меня!) Более естественной.

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


Это из-за исторических причин / соглашения

Я не знаю ни одного языка до C, который использовал C-подобный синтаксис для циклов for:

  • Донал Феллоуз отмечает, что BCPL и B не имели эквивалентной конструкции.

  • Эквиваленты FORTRAN, COBOL и Algol-60 (и Pascal) были менее выразительными и имели синтаксисы, которые не напоминали синтаксис «для» C.

Но такие языки, как C, C ++ и Java, появившиеся после C, явно заимствуют свой синтаксис "for" из C.

Стивен С
источник
Итак, философия ( ,против ;) является (слабее против сильного разрыва), а не (кортеж против делителя последовательности), верно? Тем не менее, для меня не очевидно, требуют ли аргументы или операторы более сильных разрывов (как во многих случаях для последовательности операторов, разрывы являются неявными (см., Например, JavaScript (например i++[line break]j++))), но, по крайней мере, теперь я понимаю, почему текущее соглашение не "явно перевернутый".
Петр Мигдаль
@PiotrMigdal запятая в качестве разделителя будет препятствовать использованию операнда запятой и может подразумевать, что компоненты цикла for являются операторами, а не выражениями. Это имеет значительные последствия.
Последний комментарий заставил меня задуматься о том, что делает BCPL, но, видимо, это было FOR i = e1 TO e2 BY e3 DO c(выражения e1..e3, команда c), что больше напоминает синтаксис BASIC. Источник
CVn
1
@PiotrMigdal - «Философия» - это то, о чем K & R и остальные думали еще в 1970 году. Я не думаю, что это зашло так глубоко, как вы себе представляете. (Они пытались внедрить язык «более высокого уровня», чтобы избежать необходимости писать массу программного обеспечения для телефонных коммутаторов на ассемблере.)
Стивен К.
Я только что проверил; forсинтаксис был введен в C (это не было в B или BCPL).
Donal Fellows
60

Мы пишем циклы как:

 for(x = 0; x < 10; x++)

Язык можно было бы определить так, чтобы циклы выглядели так:

 for(x = 0, x < 10, x++)

Однако подумайте о том же цикле, реализованном с использованием цикла while:

 x = 0;
 while(x < 10)
 {
     x++;
 }

Обратите внимание, что операторы x=0и x++являются окончанием точкой с запятой. Они не являются выражениями, как вы бы при вызове функции. Точки с запятой используются для разделения операторов, и поскольку два из трех элементов в цикле for являются операторами, это то, что используется там. Цикл for - это просто ярлык для такого цикла while.

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

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

x = 0, y= 3;

является совершенно допустимым утверждением даже вне цикла for. Я не знаю ни одного практического использования вне цикла for. Но дело в том, что запятые всегда делят утверждения; это не особенность цикла for.

Уинстон Эверт
источник
Конечно, я понимаю, что цикл "while" является "более фундаментальным". Но такая «короткая запись» не имеет особого смысла (по крайней мере, для меня), так как вы можете начать с x = 0; y = 0;и (внутри фигурной скобки) x++; y++;...
Петр Мигдаль
2
@PiotrMigdal, о, ты мог. Я хочу сказать, что части внутри цикла for - это операторы (разделенные точкой с запятой), а не выражения (разделенные запятыми)
Уинстон Эверт
1
Я понимаю разницу, просто для меня ;это естественно для последовательности утверждений , не обязательно разделяющих какие-либо утверждения (так это просто, что вкусы различаются?). И в нынешнем соглашении иногда случается так, что в любом случае отдельные последовательности операторов ставятся запятыми ...
Петр Мигдаль
3
@PiotrMigdal, члены структур / союзов разделены точками с запятой, но они не являются последовательными. Так что его, конечно, не ограничивается последовательностью утверждений в его использовании. В конце концов, синтаксис скорее придет на вкус.
Уинстон Эверт
Я не знаю какого-либо практического использования вне цикла for - как насчет (foo)?bar++, qux++:bletch- где вы хотите, чтобы ?:выражение делало две вещи, а не одну. Возвращаемое значение, если fooистинно qux, но оба barи quxполучают приращение.
15

В C и C ++ это оператор запятой, а не просто запятая.

Грамматика для forцикла это что-то вроде

for ([pre-expression]; [terminate-condition]; [increment-expression]) body-expression

В случае вашего вопроса:

pre-expression -> init1, init2
terminate-condition -> condition
increment-expression -> inc1, inc2

Обратите внимание, что оператор запятой позволяет вам выполнять несколько действий в одном выражении (как это видит компилятор). Если бы ваше предложение было реализовано, в грамматике была бы неоднозначность относительно того, когда программист намеревался написать оператор запятой или разделитель.

Короче говоря, ;означает конец утверждения. forЦикл является ключевым словом , за которым следует список дополнительных заявлений в окружении (). Оператор запятой-оператора позволяет использовать ,в одном операторе.

Джеймс
источник
3
Цикл for - это набор выражений, разделенных точками с запятой. Выражения могут быть намного больше, чем выражения - нельзя вставить оператор case или if в части цикла for. Важно сказать, что компоненты цикла for являются операторами, если взглянуть на bnf-форму цикла for
@MichaelT: Но в C ++ синтаксис forцикла явно допускает оператор (объявление) в качестве его первой части. (C ++ допускает объявления mid-функции, в отличие от своего предшественника C89). Вы не можете обобщать такие операторы между языками, даже для 2 языков, таких как C и C ++.
MSalters
@MichaelT Вы пропустили часть "что-то вроде"?
Джеймс
@James, вы можете избежать «чего-то подобного», используя фактический bnf for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>для C и for ( for-init-statement; conditionopt ; expressionopt ) statementдля C ++ --- ';' не только означает терминатор оператора. За циклом for не следуют операторы, заключенные в ().
8

Там нет концептуального обращения.

Точки с запятой в C представляют собой более крупные деления, чем запятые. Они разделяют заявления и декларации.

Основным делением в цикле for является то, что есть три выражения (или объявление и два выражения) и тело.

Запятые, которые вы видите в C для циклов for, не являются частью синтаксиса цикла for. Они просто проявления оператора запятой.

Запятые являются основными разделителями между аргументами в вызовах функций и между параметрами в объявлениях функций, но точки с запятой не используются. Цикл for - это специальный синтаксис; это не имеет ничего общего с функциями или вызовами функций.

Kaz
источник
2

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

Кроме того, ранее отвеченные вопросы верны, с технической точки зрения, это также потому, что в C (и C ++) запятая на самом деле является оператором , который вы можете даже перегружать . Использование точки с запятой-оператора ( operator;()) может затруднить написание компиляторов, поскольку точка с запятой - это терминатор аксиоматического выражения.

Интересным является тот факт, что запятая широко используется в качестве разделителя во всем языке. Кажется, что оператор запятой является исключением, который в основном используется для получения for-loops с несколькими работающими условиями, так в чем же дело?

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

Однако точка с запятой не разделяется - она заканчивается . И это также то, что возвращает нас к первоначальному вопросу:

for (int a = 0, float b = 0.0f; a < 100 && b < 100.0f; a++, b += 1.0f)
    printf("%d: %f", a, b);

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

Более новые языки программирования (такие как C #) могут не допускать перегрузки оператора запятой, но они, скорее всего, сохранили синтаксис, потому что его изменение кажется неестественным.

Aschratt
источник
Есть проблема с этим аргументом. В forутверждении ;символ явно используется в качестве разделителя. Он разделяет 3 синтаксические части утверждения. Нет 3-й точки с запятой, чтобы «завершить» список прогрессивных выражений. Завершается другим токеном ).
Стивен К
0

Для меня они используются более менее похожим по смыслу своим языковым смыслом. Запятые используются со списками и точками с запятой с несколькими отдельными частями.

У func(a, b, c)нас есть список аргументов.

instruction1; instruction2; instruction3 это может быть список, но список отдельных и независимых инструкций.

В то время как у for ( init1, init2; condition; inc1, inc2 )нас есть три отдельные части - список инициализаций, условие и список выражений приращения.

Людвика
источник
0

Самый простой способ увидеть это:

for(x = 0; x < 10; x++)

является:

for(
x = 0;
x < 10;
x++
)

Другими словами, эти x = 0 вещь на самом деле является оператором / инструкциями, а не параметром. Вы вставляете туда заявление. Следовательно, они разделяются точкой с запятой.

На самом деле нет никакого способа, которым они разделяются запятой. Когда в последний раз вы вставляете такие вещи, как х <10 в качестве параметра? Вы делаете это, если хотите, чтобы компьютер x <10 один раз, и вставляете результат этой операции в качестве параметра. Так что в мире запятых вы бы поставили x <10, если хотите передать значение x <0 в функцию.

Здесь вы указываете, что программа должна проверять x <10 каждый раз, когда проходит цикл. Так что это инструкция.

х ++ это определенно другая инструкция.

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

user4951
источник
Это не утверждение. Это выражение, разделенное точкой с запятой. Заявление совершенно другое.
x <10 может быть выражением (которое обычно разделяется точкой с запятой. x = 0 - это определенно оператор / инструкция.
user4951
Посмотрите на bnf для C - если цикл for был операторами, можно использовать другие операторы, такие как другой, for switchили return внутри определения цикла for (т.е. for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) - вы не можете. Это не утверждение. Вместо этого это определено какfor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>
Странный. int i = 0 - это хорошо выраженное выражение, но мы делаем это главным образом для объявления int, а именно i и присваивания ему 0 (он также возвращает 0, но как побочный эффект. Вы не можете сделать это для ({int i = 0; j = i}; j <0; cout << "Hello world") вы можете? Или да, я думаю, что вы можете.
user4951
1
@JimThio: Вы, вероятно, не знаете об этом, но «высказывание» и «выражение» имеют очень точное значение в языковых стандартах. int i = 0определенно НЕ является выражением. Набор правил, описывающих выражение, довольно сложен, учитывая, что может составлять выражение, но конструкция не TYPE NAME = EXPRESSIONсоответствует ни одному из этих правил.
MSalters