Чому крапки з комою та комами замінено для циклів?


49

Багато мов (широкий список, від C до JavaScript):

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

То чому це відображення відмінено на тих самих мовах для циклів :

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

замість (що мені здається більш природним)

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

?

Звичайно, forце ( як правило) не функція, але аргументи (тобто init, condition, increment) поводяться скоріше як аргументи функції , ніж послідовність інструкцій.

Це пов'язано з історичними причинами / конвенцією, чи є достатньо обгрунтованих міркувань взаємообміну ,та ;циклів?


1
(Моя перша публікація тут. Я не впевнений, чи належить це питання більше програмістам чи так, тому сміливо мігруйте, якщо це потрібно.)
Пьотр Мігдал

8
Це, безумовно, посада програмістів. Ласкаво просимо! :-)
Martijn Pieters

1
"Чому ні" надасть відповідь - маючи таку ж відповідь - "Тому, що комусь потрібно було зробити вибір, і такий вибір вони зробили" Те саме, що "Чому вони обрали" {"і"} "та ще 1000 інших варіантів вони зробили - "Тому що".
mattnz

2
@mattnz Питання стосується послідовності (не "Чому ми ;не використовуємо |?" (або Чому ми використовуємо "інше", а не "інакше"? )), що стосується не однієї мови, а великої їх кількості. Відповідь, що, наприклад, "це було зроблено в C як скорочений цикл, а цикл (а кілька заяв для inc були продумані лише пізніше), і люди не хотіли його змінювати, щоб уникнути роздратування програмістів", було б абсолютно чудово.
Пьотр Мігдал

Я пригадую, що читав (можливо, в K&R), що оператор комами був спочатку доданий до мови , щоб зробити можливим ініціалізувати більше однієї змінної в init-виразі оператора for.
zwol

Відповіді:


18

То чому в тих самих мовах таке відображення обернено для циклів.

Технічно відображення не "зворотне".

  • Речі, розділені комами, не є параметрами. У (принаймні) C ++ та Java вони можуть бути деклараціями, тому вони навіть не є виразами.
  • Речі, розділені крапкою з комою, не є (окремими) твердженнями.

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

То чому б не зробити це навпаки?

Я думаю, що причини випливають із "природного" значення ,та ;. В англійській письмовій мові крапкою з комою є "сильніший" розрив, ніж кома, а гліф для крапки з комою видніший, ніж кома. Ці дві речі поєднуються, щоб зробити теперішню домовленість (для мене!) Більш природною.

Але єдиним способом точно знати, чому був зроблений вибір синтаксису, було б, якщо дизайнери C могли сказати нам, про що вони думали ще в 1970 році. Я сумніваюся, що вони мають чітку пам’ять про технічні рішення, прийняті далеко назад у часі.


Це обумовлено історичними причинами / умовою

Мені не відома жодна мова до C, яка використовувала C-подібний синтаксис для циклів "for":

  • Дональд стипендіатів зазначає, що BCPL та B не мали еквівалентної конструкції.

  • Еквіваленти FORTRAN, COBOL і Algol-60 (і Pascal) були менш виразними і мали синтаксиси, що не нагадували синтаксис "C".

Але такі мови, як C, C ++ та Java, які з'явилися після C, усі чітко запозичують у 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 році. (Вони намагалися реалізувати мову "вищого рівня", щоб уникнути необхідності записувати маси програмного забезпечення для телефонних комутаторів в асемблері.)
Stephen C

Я щойно перевірив; forсинтаксис був введений в C (це не було в B або BCPL).
стипендіати Дональ

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 - це насправді те, що ви можете зробити поза циклом for.

x = 0, y= 3;

- цілком правильне твердження навіть поза циклом for. Я не знаю жодного практичного використання поза циклом for for. Але справа в тому, що коми завжди підрозділяють заяви; це не особлива особливість циклу for.


Звичайно, я розумію, що цикл "while" є "більш фундаментальним". Але таке "коротке позначення" не має особливого сенсу (принаймні для мене), як ви могли б почати з x = 0; y = 0;(і всередині фігурної дужки) x++; y++;...
Пьотр Мігдал

2
@PiotrMigdal, о, ти міг. Моя думка полягає в тому, що фрагменти всередині циклу for - це висловлювання (які розділені крапкою з комою), а не вирази (які розділені комами)
Winston Ewert,

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 є твердження, коли дивишся на bnf форму for для циклу

@MichaelT: Але в C ++ синтаксис forциклу явно дозволяє заяву (декларацію) як його першу частину. (C ++ дозволено оголошення середньої функції, на відміну від свого попередника C89). Ви не можете узагальнити такі висловлювання на різних мовах, навіть для двох мов, близьких як C і C ++.
MSalters

@MichaelT Ви пропустили частину "щось на кшталт"?
Джеймс

@James ви можете уникнути "чогось подібного", використовуючи фактичний bnf for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>для C і for ( for-init-statement; conditionopt ; expressionopt ) statementдля C ++ --- The ';' не означає лише термінатор оператора. Цикл "a" не супроводжується повідомленнями, доданими до ().

8

Концептуальної розвороту немає.

Точки з комою в С представляють більш великі поділи, ніж коми. Вони розділяють заяви та декларації.

Основні розділи в циклі for - це три вирази (або декларація та два вирази) та тіло.

Коми, які ви бачите в C для циклів, не є частиною синтаксису циклу for. Вони просто прояви оператора комами.

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


2

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

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

Що викликає таке інтерстирування, це те, що кома широко використовується як відокремлювач по всій мові. Здається, що оператор з комами - це виняток, який в основному використовується для отримання forциклів, що працюють з кількома умовами, тож у чому справа?

Насправді, operator,це побудовано для того ж, що і у визначеннях, списках аргументів тощо: Це було побудовано для відокремлення виразів - те, що синтаксична конструкція ,не може зробити. Він може відокремити лише те, що було визначено в стандарті.

Однак крапка з комою не відокремлена - вона закінчується . І це також призводить нас до початкового питання:

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

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

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


У цьому аргументі є проблема. У forвикладі ;символ просто використовується як роздільник. Він відокремлює 3 синтаксичні частини висловлювання. Не існує 3-го крапки з комою, щоб "припинити" список прогресивних виразів. Він закінчується іншим маркером - ).
Стівен C

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 thingy - це фактично твердження / інструкції, а не параметр. Ви вставляєте туди заяву. Звідси вони розділені крапкою з комою.

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

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

x ++, безумовно, ще одна інструкція.

Це всі вказівки. Тож вони розділені напівкрапкою.


Це не твердження. Це вираз, розділений крапкою з комою. Заява зовсім інша.

x <10 може бути виразом (який, як правило, розділяється крапкою з комою. x = 0, безумовно, твердження / інструкції.
user4951

Подивіться на bnf для C - якщо цикл for для циклу був висловлюваннями, ви можете використовувати інші оператори, такі як інший for switchабо return всередині визначення для циклу (тобто 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 - це вираз все в порядку, але ми робимо це головним чином, щоб оголосити інт, а саме я і призначити йому 0 (він також повертає 0, але як побічний ефект. Ви не можете цього зробити ({int i = 0; j = i}; j <0; cout << "Привіт, світ") можеш? Або так, я думаю, ти можеш.
user4951

1
@JimThio: Ви, мабуть, цього не знаєте, але "твердження" та "вираз" мають дуже точні значення в мовних стандартах. int i = 0напевно НЕ є виразом. Набір правил, що описують вираз, є досить складним, враховуючи, що може становити вираз, але конструкція не TYPE NAME = EXPRESSIONвідповідає жодному з цих правил.
MSalters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.