Pourquoi les points-virgules et les virgules sont-ils interchangés en boucles for?

49

Dans de nombreuses langues (une liste étendue, du C au JavaScript):

  • virgules ,séparer les arguments (par exemple func(a, b, c)), tandis que
  • les points-virgules ;séparent les instructions séquentielles (par exemple instruction1; instruction2; instruction3).

Alors, pourquoi ce mappage est-il inversé dans les mêmes langues pour les boucles for :

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

au lieu de (ce qui me semble plus naturel)

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

?

Bien sûr, forn’est (généralement) pas une fonction, mais les arguments (c. init-à- d . condition, increment) Se comportent davantage comme des arguments d’une fonction que comme une séquence d’instructions.

Est - ce pour des raisons historiques / une convention, ou est - il une bonne raison pour l'échange de ,et ;dans les boucles?

Piotr Migdal
la source
1
(Mon premier post ici. Je ne sais pas si cette question appartient plus aux programmeurs ou aux SO, alors n'hésitez pas à migrer, si cela est nécessaire.)
Piotr Migdal
8
C'est définitivement un post de programmeurs. Bienvenue! :-)
Martijn Pieters
1
"Why Not" fournira la réponse - en ayant la même réponse - "Parce que quelqu'un devait faire un choix et c'est son choix", comme "Pourquoi ont-ils choisi" {et "}" et 1000 autres choix ils ont fait - "Parce que".
mattnz
2
@mattnz La question concerne la cohérence (pas "Pourquoi n'utilisons-nous ;pas |?" (ou Pourquoi utilisons-nous "sinon"? " ) qui n'est pas le cas pour une langue, mais pour un grand nombre d'entre elles. Une réponse, par exemple, "cela a été fait en C comme raccourci pour la boucle While (et plusieurs énoncés pour inca été pensé que plus tard), et les gens ne voulaient pas le changer pour éviter l'irritation des programmeurs" serait parfaitement acceptable.
Piotr Migdal
Je me souviens avoir lu - peut-être dans K & R - que l'opérateur de virgule avait été ajouté à la langue pour permettre d'initialiser plus d'une variable dans l'expression-init d'une instruction for.
lundi

Réponses:

18

Alors pourquoi, dans les mêmes langues, un tel mappage est inversé pour les boucles for.

Techniquement, le mappage n'est pas "inversé".

  • Les choses séparées par des virgules ne sont pas des paramètres. En (au moins) C ++ et Java, ils peuvent être des déclarations, de sorte qu'ils ne sont même pas des expressions.
  • Les choses séparées par des points-virgules ne sont pas non plus (simples).

En réalité, nous avons ici un contexte syntaxique différent où les mêmes symboles sont utilisés différemment. Nous ne comparons pas ce qui est semblable, il n'y a donc pas de mappage ni d'argument fort en faveur d'un mappage cohérent basé sur la cohérence sémantique.

Alors pourquoi ne pas le faire dans le sens inverse?

Eh bien, je pense que les raisons viennent du sens "naturel" de ,et ;. En anglais, un point-virgule est une coupure "plus forte" qu'une virgule et le glyphe de point-virgule est plus visible qu'une virgule. Ces deux choses concourent à rendre l’arrangement actuel (pour moi!) Plus naturel.

Mais le seul moyen de savoir avec certitude pourquoi le choix de la syntaxe a été choisi serait que les concepteurs C puissent nous dire ce qu’ils pensaient en 1970. Je doute qu'ils se souviennent clairement des décisions techniques prises jusque-là.


Est-ce dû à des raisons historiques / à une convention

Je ne connais pas de langage avant C utilisant une syntaxe de type C pour les boucles "pour":

  • Donal Fellows note que BCPL et B n’avaient pas de structure équivalente.

  • Les équivalents FORTRAN, COBOL et Algol-60 (et Pascal) étaient moins expressifs et leurs syntaxes ne ressemblaient pas à la syntaxe C "pour".

Mais les langages comme C, C ++ et Java qui ont succédé à C empruntent tous clairement leur syntaxe "pour" à C.

Stephen C
la source
Donc, la philosophie de ( ,vs ;) est (coupure plus faible ou plus forte), plutôt que (séparateur de tuple contre séquence), non? Pour moi, il n’est pas évident de savoir si les arguments ou les déclarations nécessitent des ruptures plus fortes (comme dans de nombreux cas pour une séquence de déclarations, les ruptures sont implicites (voir, par exemple, JavaScript (exemple i++[line break]j++))), mais au moins, je comprends maintenant pourquoi la convention actuelle n'est pas "évidemment inversé".
Piotr Migdal
@PiotrMigdal la virgule en tant que délimiteur empêcherait l'utilisation de l'opérande virgule et pourrait impliquer que les composants de la boucle for sont des instructions plutôt que des expressions. Cela a des implications importantes.
Le dernier commentaire m'a rendu curieux de voir ce que BCPL a fait, mais apparemment, c'était le cas FOR i = e1 TO e2 BY e3 DO c(expressions e1..e3, commande c), qui ressemble davantage à la syntaxe de BASIC. Source
un CVn
1
@PiotrMigdal - La "philosophie" est ce que K & R et le reste pensaient en 1970. Je ne pense pas que cela aille au fond de la pensée que vous imaginez. (Ils essayaient d'implémenter un langage "de plus haut niveau" pour éviter d'avoir à écrire des logiciels de commutation téléphonique en assembleur.)
Stephen C
Je viens de vérifier; la forsyntaxe a été introduite en C (ce n'était pas en B ou BCPL).
Donal Fellows
60

Nous écrivons des boucles comme:

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

Le langage aurait pu être défini de manière à ce que les boucles ressemblent à:

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

Cependant, pensez à la même boucle implémentée en utilisant une boucle while:

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

Notez que les instructions x=0and x++sont terminées par des points-virgules. Ce ne sont pas des expressions comme celles que vous auriez dans un appel de fonction. Les points-virgules sont utilisés pour séparer des instructions, et comme deux des trois éléments d'une boucle for sont des instructions, c'est ce qui est utilisé ici. Une boucle for n'est qu'un raccourci pour une telle boucle while.

De plus, les arguments n'agissent pas vraiment comme des arguments d'une fonction. Les deuxième et troisième sont évalués à plusieurs reprises. Il est vrai qu'ils ne sont pas une séquence, mais ils ne sont pas non plus des arguments de fonction.

En outre, le fait que vous puissiez utiliser des virgules pour avoir plusieurs instructions dans la boucle for est en réalité quelque chose que vous pouvez faire en dehors de la boucle for.

x = 0, y= 3;

est une déclaration parfaitement valable même en dehors d'une boucle for. Je ne connais cependant aucune utilisation pratique en dehors de la boucle for. Mais le fait est que les virgules subdivisent toujours les instructions; ce n'est pas une particularité de la boucle for.

Winston Ewert
la source
Bien sûr, je comprends que la boucle "While" est "plus fondamentale". Mais une telle "notation abrégée" n'a pas beaucoup de sens (du moins pour moi), car vous pourriez commencer avec x = 0; y = 0;et (entre les crochets) x++; y++;...
Piotr Migdal
2
@ PiotrMigdal, oh tu pourrais. Mon point est que les morceaux dans la boucle for sont des instructions (qui sont séparées par des points-virgules) et non des expressions (qui sont séparées par des virgules)
Winston Ewert
1
Je comprends la différence, rien que pour moi ;est naturel pour une séquence d’énoncés , pas nécessairement en séparant les énoncés (c’est pourquoi les goûts diffèrent-ils?). Et dans la convention actuelle, on finit parfois par séparer des séquences d'instructions avec des virgules ...
Piotr Migdal
3
@PiotrMigdal, les membres des structures / unions sont séparés par des points-virgules, mais ils ne sont pas vraiment séquentiels. Donc, ce n'est certainement pas limité à une séquence d'énoncé dans son utilisation. En fin de compte, la syntaxe revient plutôt au goût.
Winston Ewert
Cependant, je ne connais aucune utilisation pratique en dehors de la boucle for (foo)?bar++, qux++:bletch--- Et si vous voulez que l' ?:expression fasse deux choses plutôt qu'une seule. La valeur de retour si footrue est qux, mais les deux baret quxsont incrémentés.
15

En C et C ++, il s'agit de l'opérateur de virgule, pas simplement d'une virgule.

La grammaire pour une forboucle est quelque chose comme

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

Dans le cas de votre question:

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

Notez que l'opérateur de virgule vous permet d'effectuer plusieurs actions dans une instruction (comme le compilateur le voit). Si votre suggestion était mise en œuvre, la grammaire serait ambiguë quant au moment où le programmeur a l'intention d'écrire une instruction opérateur par virgule ou un séparateur.

En bref, ;signifie la fin d'une déclaration. Une forboucle est un mot-clé suivi d'une liste d'instructions facultatives entourées par (). L'instruction virgule-opérateur permet l'utilisation de ,dans une seule instruction.

James
la source
3
Une boucle for est un ensemble d'expressions séparées par des points-virgules. Les instructions peuvent être beaucoup plus que des expressions - on ne peut pas glisser une instruction case ou if dans une partie de boucle for. C'est une implication significative de dire que les composants de la boucle for sont des instructions quand on regarde la forme bnf d'une boucle for
@MichaelT: Mais en C ++, la syntaxe d'une forboucle autorise explicitement une déclaration (déclaration) en tant que première partie. (C ++ autorisait les déclarations de moyenne fonction, contrairement à son prédécesseur C89). Vous ne pouvez pas généraliser de telles instructions à travers les langages, même pour 2 langages aussi proches que C et C ++.
MSalters
@MichaelT Avez-vous oublié la partie "est quelque chose comme"?
James
Vous pouvez éviter @ James le « quelque chose comme » en utilisant le bnf réelle de for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>pour C et for ( for-init-statement; conditionopt ; expressionopt ) statementpour C ++ --- Le « ; » ne signifie pas seulement un terminateur de déclaration. Une boucle for n'est pas suivie d'instructions entre ().
8

Il n'y a pas d'inversion conceptuelle.

Les points-virgules en C représentent plus de divisions principales que de virgules. Ils séparent les déclarations et les déclarations.

Les principales divisions de la boucle for sont qu'il existe trois expressions (ou une déclaration et deux expressions) et un corps.

Les virgules que vous voyez dans les boucles for for ne font pas spécifiquement partie de la syntaxe de la boucle for. Ce ne sont que des manifestations de l'opérateur virgule.

Les virgules sont des séparateurs majeurs entre les arguments dans les appels de fonction et entre les paramètres dans les déclarations de fonction, mais les points-virgules ne sont pas utilisés. La boucle for est une syntaxe spéciale; cela n'a rien à voir avec des fonctions ou des appels de fonctions.

Kaz
la source
2

C'est peut-être quelque chose de spécifique pour C / C ++, mais je poste cette réponse, car la syntaxe des lagnages que vous avez décrits est principalement influencée par la syntaxe C.

En plus des réponses précédentes aux questions précédentes, d’un point de vue technique, c’est aussi parce qu’en C (et C ++), la virgule est en fait un opérateur , que vous pouvez même surcharger . L'utilisation d'un opérateur point-virgule ( operator;()) rendrait probablement plus difficile l'écriture de compilateurs, car le point-virgule est le terminateur d'expression axiomatique.

Ce qui rend ce intersting est le fait que la virgule est largement utilisé comme seperator partout dans la langue. Il semble que l'opérateur de virgule soit une exception, principalement utilisée pour faire fonctionner les forboucles avec plusieurs conditions, alors quel est le problème?

En fait, il operator,est conçu pour faire la même chose que dans les définitions, les listes d'arguments, etc.: Il a été construit pour séparer les expressions - ce que la construction syntaxique ,ne peut pas faire. Il ne peut séparer que ce qui a été défini dans la norme.

Cependant, le point-virgule ne sépare pas - il se termine . Et c’est aussi ce qui nous ramène à la question initiale:

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

La virgule sépare les expressions dans les trois parties de la boucle, tandis que le point-virgule termine une partie (initialisation, condition ou réflexion ultérieure) de la définition de la boucle.

Les nouveaux langages de programmation (tels que C #) ne permettent peut-être pas de surcharger l'opérateur virgule, mais ils conservent probablement la syntaxe, car leur modification semble quelque peu anormale.

Aschratt
la source
Il y a un problème avec cet argument. Dans une fordéclaration, le ;symbole est clairement utilisé comme séparateur. Il sépare les 3 parties syntaxiques de la déclaration. Il n'y a pas de 3ème point-virgule pour "terminer" la liste des expressions progressives. Il se termine par un autre jeton - ).
Stephen C
0

Pour moi, ils sont utilisés de manière moins semblable à leur sens linguistique. Les virgules sont utilisées avec des listes et des points-virgules avec des parties plus séparées.

En func(a, b, c)nous avons une liste d'arguments.

instruction1; instruction2; instruction3 est peut-être une liste mais une liste d'instructions séparées et indépendantes.

Alors que dans for ( init1, init2; condition; inc1, inc2 )nous avons trois parties distinctes - une liste des initialisations, une condition et une liste d'expressions incrément.

Ludwika
la source
0

Le moyen le plus simple de le voir est le suivant:

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

est:

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

En d'autres termes, ces x = 0 thingy sont en fait une instruction / des instructions plutôt qu'un paramètre. Vous insérez une déclaration ici. Ils sont donc séparés par un point-virgule.

En fait, il n’ya aucun moyen de les séparer par une virgule. Quand insérez-vous des éléments comme x <10 en tant que paramètre? Vous faites cela si vous voulez utiliser l’ordinateur x <10 une fois et insérer le résultat de cette opération en tant que paramètre. Donc, dans le monde des virgules, vous mettriez x <10 si vous voulez transmettre la valeur de x <0 à une fonction.

Ici, vous spécifiez que le programme doit vérifier x <10 à chaque fois que la boucle est passée. Donc, c'est une instruction.

x ++ est certainement une autre instruction.

Ce sont toutes des instructions. Donc, ils sont séparés par un point-virgule.

utilisateur4951
la source
Ce n'est pas une déclaration. C'est une expression séparée par un point-virgule. Une déclaration est complètement différente.
x <10 peut être une expression (qui est généralement séparée par un point-virgule. x = 0 est définitivement une instruction / des instructions.
user4951
Regardez le fichier bnf pour C - si la boucle for était une instruction, vous pouvez utiliser d'autres instructions telles qu'une autre for switchou return à l' intérieur de la définition de la boucle for (c'est-à-dire for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) --- vous ne pouvez pas. Ce n'est pas une déclaration. Au lieu de cela, il est défini commefor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>
Étrange. int i = 0 est une expression correcte, mais nous le faisons principalement pour déclarer un int, à savoir i, et lui affecter 0 (il renvoie également 0, mais en tant qu’effet secondaire. Vous ne pouvez pas faire pour ({int i = 0; j = i}; j <0; cout << "Bonjour tout le monde") pouvez-vous? Ou oui, je pense que vous le pouvez.
user4951
1
@ JimThio: Vous n'êtes probablement pas au courant, mais "déclaration" et "expression" ont une signification très précise dans les normes de langage. int i = 0n'est certainement pas une expression. L'ensemble de règles décrivant une expression est assez complexe, considérant ce qui peut constituer une expression, mais la construction TYPE NAME = EXPRESSIONne correspond à aucune de ces règles.
MSalters