¿Por qué los puntos y comas se intercambian por bucles?

49

En muchos lenguajes (una amplia lista, de C a JavaScript):

  • las comas ,separan los argumentos (por ejemplo func(a, b, c)), mientras que
  • punto y coma ;separan instrucciones secuenciales (p instruction1; instruction2; instruction3. ej .).

Entonces, ¿por qué esta asignación se invierte en los mismos idiomas para bucles for :

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

en lugar de (lo que me parece más natural)

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

?

Claro, fores (normalmente) no una función, pero los argumentos (es decir init, condition, increment) se comportan más como argumentos de una función de una secuencia de instrucciones.

¿Se debe a razones históricas / una convención, o hay una buena razón para el intercambio de ,y ;en bucles?

Piotr Migdal
fuente
1
(Mi primera publicación aquí. No estoy seguro de si esta pregunta pertenece más a Programadores o SO, así que siéntase libre de migrar, si es necesario).
Piotr Migdal
8
Esta es definitivamente una publicación de programadores. ¡Bienvenidos! :-)
Martijn Pieters
1
"Por qué no" proporcionará la respuesta, al tener la misma respuesta, "Porque alguien necesitaba tomar una decisión, y esa es la elección que hizo" Igual que "Por qué eligió" {"y"} "y 1000 otras opciones hicieron - "Porque".
mattnz
2
@mattnz La pregunta es acerca de la consistencia (no "¿Por qué usamos ;no |?" (o ¿Por qué usamos 'otra cosa' no 'en otro caso'? )) que no es el caso de un idioma, pero un gran número de ellos. Una respuesta que, por ejemplo, "se hizo en C como una abreviatura del ciclo while (y las declaraciones múltiples para inc se pensaron solo más tarde), y la gente no quería cambiarla para evitar la irritación de los programadores", estaría perfectamente bien.
Piotr Migdal
Recuerdo haber leído, tal vez en K&R, que el operador de coma se agregó originalmente al lenguaje para hacer posible inicializar más de una variable en la expresión init de una declaración for.
zwol

Respuestas:

18

Entonces, ¿por qué en los mismos idiomas este mapeo se invierte para los bucles?

Técnicamente, el mapeo no se "invierte".

  • Las cosas separadas por comas no son parámetros. En (al menos) C ++ y Java, pueden ser declaraciones, por lo que ni siquiera son expresiones.
  • Las cosas separadas por punto y coma tampoco son declaraciones (únicas).

En realidad, lo que tenemos aquí es un contexto sintáctico diferente donde los mismos símbolos se usan de manera diferente. No estamos comparando like con like, por lo que no hay mapeo ni argumento sólido para un mapeo consistente basado en la consistencia semántica.

Entonces, ¿por qué no hacerlo al revés?

Bueno, creo que las razones provienen del significado "natural" de ,y ;. En el idioma escrito en inglés, un punto y coma es una ruptura "más fuerte" que una coma, y ​​el glifo para punto y coma es más visible que una coma. Esas dos cosas se combinan para hacer que la disposición actual parezca (¡para mí!) Más natural.

Pero la única forma de saber con certeza por qué se hizo la elección de la sintaxis sería si los diseñadores de C pudieran decirnos qué estaban pensando en ~ 1970. Dudo que tengan un claro recuerdo de las decisiones técnicas tomadas tan atrás en el tiempo.


¿Se debe a razones históricas / una convención?

No conozco ningún lenguaje antes de C que use una sintaxis similar a C para bucles "for":

  • Donal Fellows señala que BCPL y B no tenían una construcción equivalente.

  • Los equivalentes FORTRAN, COBOL y Algol-60 (y Pascal) fueron menos expresivos y tenían sintaxis que no se parecían a la sintaxis C "para".

Pero los lenguajes como C, C ++ y Java que surgieron después de C, claramente toman prestada su sintaxis "for" de C.

Stephen C
fuente
Entonces, la filosofía de ( ,vs ;) es (ruptura más débil versus más fuerte), en lugar de (divisor de secuencia vs tupla), ¿verdad? Aún así, para mí no es obvio si los argumentos o las declaraciones necesitan pausas más fuertes (como en muchos casos para una secuencia de declaraciones, las pausas son implícitas (ver, por ejemplo, JavaScript (p i++[line break]j++. Ej .)), Pero al menos ahora entiendo por qué la convención actual no está "obviamente invertido".
Piotr Migdal
@PiotrMigdal la coma como delimitador evitaría el uso del operando de la coma y podría implicar que los componentes del bucle for son declaraciones en lugar de expresiones. Esto tiene implicaciones significativas.
El último comentario me hizo sentir curiosidad por lo que hizo BCPL, pero aparentemente allí estaba FOR i = e1 TO e2 BY e3 DO c(expresiones e1..e3, comando c), que se parece más a la sintaxis de BASIC. Fuente
un CVn el
1
@PiotrMigdal: la "filosofía" es lo que K&R y el resto estaban pensando en 1970. No creo que haya llegado a la profundidad de pensamiento que imaginas. (Intentaban implementar un lenguaje de "nivel superior" para evitar tener que escribir grandes cantidades de software de conmutador telefónico en el ensamblador)
Stephen C
Acabo de verificar; La forsintaxis se introdujo en C (no en B o BCPL).
Donal Fellows
60

Escribimos bucles como:

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

El lenguaje podría haberse definido para que los bucles se vean así:

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

Sin embargo, piense en el mismo bucle implementado usando un bucle while:

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

Observe que las declaraciones x=0y x++son, terminadas en punto y coma. No son expresiones como las que tendría en una llamada de función. Los puntos y comas se usan para separar las declaraciones, y dado que dos de los tres elementos en un bucle for son declaraciones, eso es lo que se usa allí. Un bucle for es solo un atajo para tal bucle while.

Además, los argumentos realmente no actúan como argumentos para una función. El segundo y el tercero son evaluados repetidamente. Es cierto que no son una secuencia, pero tampoco son argumentos de función.

Además, el hecho de que puede usar comas para tener varias declaraciones en el ciclo for es en realidad algo que puede hacer fuera del ciclo for.

x = 0, y= 3;

es una declaración perfectamente válida incluso fuera de un ciclo for. Sin embargo, no conozco ningún uso práctico fuera del ciclo for. Pero el punto es que las comas siempre subdividen las declaraciones; No es una característica especial del bucle for.

Winston Ewert
fuente
Claro, entiendo que el bucle "while" es "más fundamental". Pero esa "notación de mano corta" no tiene mucho sentido (al menos para mí), ya que podría comenzar con x = 0; y = 0;y (dentro del corchete) x++; y++;...
Piotr Migdal
2
@PiotrMigdal, oh, podrías. Mi punto es que las piezas dentro del bucle for son declaraciones (que están separadas por punto y coma) no expresiones (que están separadas por comas)
Winston Ewert
1
Obtengo la diferencia, solo para mí ;es natural para una secuencia de declaraciones , no necesariamente separando ninguna declaración (entonces, ¿es solo que los gustos difieren?). Y en la convención actual, uno ocasionalmente termina separando secuencias de declaraciones con comas de todos modos ...
Piotr Migdal
3
@PiotrMigdal, los miembros de estructuras / uniones están separados por punto y coma, pero esos no son realmente secuenciales. Por lo tanto, ciertamente no está restringido a una secuencia de afirmación en su uso. Al final del día, la sintaxis se reduce al gusto.
Winston Ewert
Sin embargo, no conozco ningún uso práctico fuera del ciclo for --- ¿Qué tal (foo)?bar++, qux++:bletch--- dónde quieres que la ?:expresión haga dos cosas en lugar de solo una? El valor de retorno si fooes verdadero es qux, pero ambos bary quxse incrementan.
15

En C y C ++, este es el operador de coma, no solo una coma.

La gramática para un forbucle es algo así como

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

En el caso de su pregunta:

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

Tenga en cuenta que el operador de coma le permite realizar múltiples acciones en una declaración (como lo ve el compilador). Si su sugerencia se implementara, habría una ambigüedad en la gramática sobre cuándo el programador tenía la intención de escribir una declaración de operador de coma o un separador.

En resumen, ;significa el final de una declaración. Un forbucle es una palabra clave seguida de una lista de declaraciones opcionales rodeadas por (). La declaración de operador de coma permite el uso de ,en una sola declaración.

James
fuente
3
Un bucle for es un conjunto de expresiones separadas por punto y coma. Las declaraciones pueden ser mucho más que expresiones: uno no puede deslizar una declaración de caso o si la declaración en un bucle for partes Es una implicación significativa decir que los componentes del ciclo for son declaraciones cuando uno mira la forma bnf de un ciclo for
@MichaelT: Pero en C ++, la sintaxis de un forbucle permite explícitamente una declaración (declaración) como su primera parte. (C ++ permitió declaraciones en la mitad de la función, a diferencia de su predecesor C89). No puede generalizar dichas declaraciones en todos los idiomas, incluso para 2 idiomas tan cercanos como C y C ++.
MSalters
@MichaelT ¿Te perdiste la parte 'es algo así'?
James
@ James puede evitar el "algo así como" utilizando el BNF real de for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>de C y for ( for-init-statement; conditionopt ; expressionopt ) statementde C ++ --- El ';' no solo significa un terminador de declaración. Un ciclo for no va seguido de declaraciones encerradas en ().
8

No hay inversión conceptual.

Los puntos y coma en C representan divisiones más importantes que las comas. Separan declaraciones y declaraciones.

Las divisiones principales en el ciclo for es que hay tres expresiones (o una declaración y dos expresiones) y un cuerpo.

Las comas que ves en C para bucles no son parte de la sintaxis del bucle for específicamente. Son solo manifestaciones del operador de coma.

Las comas son separadores principales entre argumentos en llamadas a funciones y entre parámetros en declaraciones de funciones, pero no se utilizan punto y coma. El bucle for es una sintaxis especial; No tiene nada que ver con funciones o llamadas a funciones.

Kaz
fuente
2

Tal vez esto sea algo específico para C / C ++, pero publico esta respuesta, porque la sintaxis de los idiomas que describió está influenciada principalmente por la sintaxis C.

Además de que las preguntas respondidas anteriormente son ciertas, desde un punto de vista técnico, eso también se debe a que en C (y C ++) la coma es en realidad un operador , que incluso puede sobrecargar . El uso de un operador de punto y coma ( operator;()) posiblemente dificultaría la escritura de compiladores, ya que el punto y coma es el terminador de expresión axiomático.

Lo que hace que esto sea interesante es el hecho de que la coma se usa ampliamente como separador en todo el lenguaje. Parece que el operador de coma es una excepción, que se usa principalmente para que forfuncionen los bucles con múltiples condiciones, entonces, ¿cuál es el problema?

De hecho, operator,está diseñado para hacer lo mismo que en las definiciones, listas de argumentos, etc.: ha sido creado para separar expresiones, algo que la construcción sintáctica ,no puede hacer. Solo puede separar lo que se ha definido en el estándar.

Sin embargo, el punto y coma no se separa, termina . Y esto también es lo que nos lleva de vuelta a la pregunta original:

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

La coma separa las expresiones en las tres partes del bucle, mientras que el punto y coma termina una parte (inicialización, condición o idea de último momento) de la definición del bucle.

Es posible que los lenguajes de programación más nuevos (como C #) no permitan sobrecargar el operador de coma, pero lo más probable es que mantuvieran la sintaxis, porque cambiarlo no parece natural.

Aschratt
fuente
Hay un problema con este argumento. En una fordeclaración, el ;símbolo se usa claramente como separador. Separa las 3 partes sintácticas de la declaración. No hay un tercer punto y coma para "terminar" la lista de expresiones progresivas. Está terminado por un token diferente - ).
Stephen C
0

Para mí se usan más significados menos similares a su sentido lingüístico. Las comas se usan con listas y punto y coma con partes más separadas.

En func(a, b, c)tenemos una lista de argumentos.

instruction1; instruction2; instruction3 es quizás una lista pero una lista de instrucciones separadas e independientes.

Mientras for ( init1, init2; condition; inc1, inc2 )que tenemos tres partes separadas: una lista de inicializaciones, una condición y una lista de expresiones de incremento.

ludwika
fuente
0

La forma más fácil de verlo es la siguiente:

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

es:

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

En otras palabras, esas cosas x = 0 son en realidad una declaración / instrucciones en lugar de un parámetro. Inserta una declaración allí. Por lo tanto, están separados por punto y coma.

De hecho, no hay forma de que estén separados por comas. ¿Cuándo hace la última vez que inserta cosas como x <10 como parámetro? Lo hace si desea computarizar x <10 una vez e insertar el resultado de esa operación como parámetro. Entonces, en el mundo de las comas, pondría x <10 si desea pasar el valor de x <0 a una función.

Aquí especifica que el programa debe verificar x <10 cada vez que se pasa el bucle. Entonces eso es una instrucción.

x ++ es definitivamente otra instrucción.

Esas son todas las instrucciones. Entonces están separados por punto y coma.

user4951
fuente
No es una declaración. Es una expresión separada por un punto y coma. Una declaración es completamente diferente.
x <10 puede ser una expresión (que generalmente estaría separada por punto y coma. x = 0 es definitivamente una declaración / instrucciones.
user4951
Mire el bnf para C : si el ciclo for era declaraciones, uno podría usar otras declaraciones como otra for switcho return dentro de la definición del ciclo for (es decir for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) --- no puede. No es una declaración. En cambio, se define comofor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>
Extraño. int i = 0 es una expresión correcta, pero lo hacemos principalmente para declarar un int, es decir, i y asignarle 0 (también devuelve 0 pero como un efecto secundario. No se puede hacer para ({int i = 0; j = i}; j <0; cout << "Hola mundo") ¿puedes? O sí, creo que puedes.
user4951
1
@ JimThio: Probablemente no lo sepas, pero "declaración" y "expresión" tienen significados muy precisos en los estándares del lenguaje. int i = 0Definitivamente NO es una expresión. El conjunto de reglas que describen una expresión es bastante complejo, considerando lo que puede constituir una expresión, pero la construcción TYPE NAME = EXPRESSIONno coincide con ninguna de esas reglas.
MSalters