Por que ponto e vírgula e vírgulas são trocadas por loops?


49

Em vários idiomas (uma lista ampla, de C a JavaScript):

  • vírgulas ,separam argumentos (por exemplo func(a, b, c)), enquanto
  • ponto e vírgula ;separam instruções seqüenciais (por exemplo instruction1; instruction2; instruction3).

Então, por que esse mapeamento é revertido nos mesmos idiomas para loops for :

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

em vez de (o que me parece mais natural)

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

?

Claro, foré (geralmente) não é uma função, mas os argumentos (ou seja init, condition, increment) se comportam mais como argumentos de uma função do que uma seqüência de instruções.

Não é devido a razões históricas / uma convenção, ou é uma boa razão para a troca de ,e ;em loops?


11
(. Meu post F1rst aqui estou eu não tenho certeza se esta questão pertence mais a programadores ou SO, então sinta-se livre para migrar, se for necessário.)
Piotr Migdal

8
Este é definitivamente um post de programadores. Bem vinda! :-)
Martijn Pieters

11
"Por que não" fornecerá a resposta - por ter a mesma resposta - "Porque alguém precisava fazer uma escolha, e essa é a escolha que fez" Igual a "Por que eles escolheram" {"e"} "e 1000 outras opções eles criaram - "Porque".
mattnz

2
@mattnz A questão é sobre consistência (não "Por que ;não usamos |?" (ou Por que usamos 'else', não 'caso contrário'? )), que não é o caso de um idioma, mas de um grande número deles. Uma resposta que, por exemplo, "foi feita em C como um atalho para o loop while (e várias declarações para inc foram pensadas apenas mais tarde), e as pessoas não quiseram alterá-lo para evitar a irritação dos programadores" seria perfeitamente aceitável.
Piotr Migdal

Lembro-me de ler - talvez em K&R - que o operador de vírgula foi originalmente adicionado ao idioma para possibilitar a inicialização de mais de uma variável na expressão init de uma instrução for.
Zwol 22/04/2013

Respostas:


18

Então, por que nos mesmos idiomas esse mapeamento é revertido para loops.

Tecnicamente, o mapeamento não é "revertido".

  • As coisas separadas por vírgulas não são parâmetros. Em (pelo menos) C ++ e Java, eles podem ser declarações, portanto, nem são expressões.
  • As coisas separadas por ponto e vírgula também não são declarações (únicas).

Na realidade, o que temos aqui é um contexto sintático diferente, onde os mesmos símbolos estão sendo usados ​​de maneira diferente. Não estamos comparando like com like, portanto não há mapeamento e nenhum argumento forte para um mapeamento consistente com base na consistência semântica.

Então, por que não fazer o contrário?

Bem, acho que as razões vêm do significado "natural" de ,e ;. No idioma escrito em inglês, um ponto e vírgula é uma pausa "mais forte" que uma vírgula, e o glifo para ponto e vírgula é mais visível que uma vírgula. Essas duas coisas se combinam para fazer com que o arranjo atual pareça (para mim!) Ser mais natural.

Mas a única maneira de saber ao certo por que a escolha da sintaxe foi feita seria se os designers de C pudessem nos dizer o que estavam pensando em 1970. Duvido que eles tenham uma clara memória de decisões técnicas tomadas tão longe no tempo.


É por razões históricas / uma convenção

Não tenho conhecimento de nenhum idioma antes de C que usasse uma sintaxe semelhante a C para loops "for":

  • Donal Fellows observa que BCPL e B não tinham uma construção equivalente.

  • Os equivalentes FORTRAN, COBOL e Algol-60 (e Pascal) eram menos expressivos e tinham sintaxes que não se assemelhavam à sintaxe C "para".

Mas linguagens como C, C ++ e Java que vieram depois de C claramente emprestam sua sintaxe "for" de C.


Então, a filosofia de ( ,vs ;) é (quebra mais fraca vs. Ainda para mim, não é óbvio se os argumentos ou declarações precisam de interrupções mais fortes (como em muitos casos, para uma sequência de declarações, as interrupções estão implícitas (consulte, por exemplo, JavaScript (por exemplo i++[line break]j++))), mas pelo menos agora entendi o porquê da convenção atual não é "obviamente revertido".
Piotr Migdal

@PiotrMigdal a vírgula como delimitador impediria o uso do operando de vírgula e poderia implicar que os componentes do loop for são instruções, e não expressões. Isso tem implicações significativas.

O último comentário me deixou curioso sobre o que o BCPL fez, mas aparentemente havia FOR i = e1 TO e2 BY e3 DO c(expressões e1..e3, comando c), que mais se assemelha à sintaxe do BASIC. Origem
a CVn

11
@PiotrMigdal - A "filosofia" é o que quer que K&R e o resto pensassem em 1970. Não acho que tenha chegado à profundidade do pensamento que você imagina. (Eles estavam tentando implementar uma linguagem "mais alto nível" para evitar ter de massas de escrita de software central telefônica em assembler.)
Stephen C

Eu acabei de verificar; a forsintaxe foi introduzida em C (não estava em B ou BCPL).
Donal Fellows

60

Escrevemos loops como:

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

O idioma poderia ter sido definido para que os loops parecessem:

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

No entanto, pense no mesmo loop implementado usando um loop while:

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

Observe que as instruções x=0e x++são terminadas por ponto e vírgula. Eles não são expressões como você teria em uma chamada de função. Ponto-e-vírgula são usados ​​para separar instruções, e como dois dos três elementos em um loop for são instruções, é isso que é usado lá. Um loop for é apenas um atalho para um loop while.

Além disso, os argumentos realmente não agem como argumentos para uma função. O segundo e o terceiro são avaliados repetidamente. É verdade que eles não são uma sequência, mas também não são argumentos de função.

Além disso, o fato de que você pode usar vírgulas para ter várias instruções no loop for é realmente algo que você pode fazer fora do loop for.

x = 0, y= 3;

é uma declaração perfeitamente válida mesmo fora de um loop for. Não conheço nenhum uso prático fora do loop for. Mas o ponto é que vírgulas sempre subdividem declarações; não é uma característica especial do loop for.


Claro, eu entendo que o loop "while" é "mais fundamental". Mas tal "notação de curto mão" não faz muito sentido (pelo menos para mim), como você poderia começar com x = 0; y = 0;e (dentro do colchete) x++; y++;...
Piotr Migdal

2
@PiotrMigdal, oh você poderia. Meu ponto é que as peças para dentro do circuito são demonstrações (que são separadas por um ponto e vírgula) não expressões (que estão separados por vírgulas)
Winston Ewert

11
Percebo a diferença, apenas para mim ;é natural para uma sequência de afirmações , não necessariamente separando quaisquer afirmações (será que o gosto é diferente?). E no actual uma convenção, ocasionalmente, acaba com a separação seqüências de declarações com vírgulas de qualquer maneira ...
Piotr Migdal

3
@PiotrMigdal, os membros de estruturas / uniões são separados por ponto e vírgula, mas esses não são realmente sequenciais. Portanto, certamente não está restrito a uma sequência de afirmações em seu uso. No final do dia, a sintaxe se resume ao gosto.
Winston Ewert

Eu não conheço nenhum uso prático fora do loop for --- Que tal (foo)?bar++, qux++:bletch--- Onde você deseja que a ?:expressão faça duas coisas em vez de apenas uma. O valor de retorno se foofor verdadeiro é qux, mas ambos bare quxsão incrementados.

15

Em C e C ++, esse é o operador de vírgula, não apenas uma vírgula.

A gramática de um forloop é algo como

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

No caso de sua pergunta:

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

Observe que o operador de vírgula permite executar várias ações em uma instrução (como o compilador vê). Se sua sugestão foi implementada, haveria uma ambiguidade na gramática sobre quando o programador pretendia escrever uma declaração de operador de vírgula ou um separador.

Em resumo, ;significa o fim de uma declaração. Um forloop é uma palavra-chave seguida por uma lista de instruções opcionais cercadas por (). A instrução vírgula-operador permite o uso de ,em uma única instrução.


3
Um loop for é um conjunto de expressões separadas por ponto e vírgula. As instruções podem ser muito mais do que expressões - não se pode inserir uma declaração de caso ou se em uma parte do loop for. É uma implicação significativa para dizer que os componentes do loop for são declarações quando se olha para a forma bnf de um loop

@ MichaelT: Mas em C ++ a sintaxe de um forloop permite explicitamente uma declaração (declaração) como sua primeira parte. (C ++ permitiu declarações com função intermediária, em oposição ao seu predecessor C89). Você não pode generalizar essas declarações entre idiomas, mesmo em dois idiomas tão próximos quanto C e C ++.
MSalters

@MichaelT Você sentiu falta da parte 'é algo parecido'?
James

@ James você pode evitar o "algo como" usando a BNF real for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>para C e for ( for-init-statement; conditionopt ; expressionopt ) statementde C ++ --- O ';' não significa apenas um terminador de instrução. Um loop for não é seguido por instruções incluídas em ().

8

Não há reversão conceitual.

O ponto e vírgula em C representa mais divisões principais do que vírgulas. Eles separam declarações e declarações.

As principais divisões no loop for são que existem três expressões (ou uma declaração e duas expressões) e um corpo.

As vírgulas que você vê em C para loops não fazem parte da sintaxe do loop for especificamente. São apenas manifestações do operador de vírgula.

Vírgulas são os principais separadores entre argumentos em chamadas de função e entre parâmetros em declarações de função, mas ponto e vírgula não é usado. O loop for é uma sintaxe especial; não tem nada a ver com funções ou chamadas de função.


2

Talvez isso seja algo específico para C / C ++, mas eu posto esta resposta, porque a sintaxe dos atrasos que você descreveu é influenciada principalmente pela sintaxe C.

Além das perguntas respondidas anteriormente serem verdadeiras, do ponto de vista técnico, também porque em C (e C ++) a vírgula é realmente um operador , que você pode até sobrecarregar . Usar um ponto-e-vírgula-operador ( operator;()) possivelmente tornaria mais difícil escrever compiladores, pois o ponto-e-vírgula é o terminador de expressões axiomáticas.

O que torna isso interessante é o fato de que a vírgula é amplamente usada como separador em todo o idioma. Parece que o operador de vírgula é uma exceção, usado principalmente para obter forloops com várias condições funcionando, então qual é o problema?

De fato, ele operator,é construído para fazer o mesmo que em definições, listas de argumentos e assim por diante: Ele foi construído para separar expressões - algo que a construção sintática ,não pode fazer. Só pode separar o que foi definido no padrão.

No entanto, o ponto e vírgula não se separa - ele termina . E é também isso que nos leva de volta à pergunta original:

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

A vírgula separa as expressões nas três partes do loop, enquanto o ponto e vírgula termina uma parte (inicialização, condição ou reflexão posterior) da definição do loop.

As linguagens de programação mais recentes (como C #) podem não permitir sobrecarregar o operador de vírgula, mas elas provavelmente mantiveram a sintaxe, porque a alteração parece algo antinatural.


Há um problema com esse argumento. Em uma fordeclaração, o ;símbolo é usado claramente como um separador. Separa as três partes sintáticas da declaração. Não existe um terceiro ponto e vírgula para "finalizar" a lista de expressões progressivas. É finalizado por um token diferente - ).
Stephen C

0

Para mim, eles usam um significado menos parecido com o sentido linguístico. Vírgulas são usadas com listas e ponto e vírgula com partes mais separadas.

Em func(a, b, c)nós temos uma lista de argumentos.

instruction1; instruction2; instruction3 talvez seja uma lista, mas uma lista de instruções separadas e independentes.

Enquanto for ( init1, init2; condition; inc1, inc2 )temos três partes separadas - uma lista de inicializações, uma condição e uma lista de expressões de incremento.


0

A maneira mais fácil de ver isso é a seguinte:

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

é:

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

Em outras palavras, essas coisas x = 0 são na verdade uma declaração / instruções, e não um parâmetro. Você insere uma declaração lá. Portanto, eles são separados por ponto e vírgula.

De fato, não há como eles serem separados por vírgula. Quando a última vez que você insere itens como x <10 como parâmetro? Faça isso se desejar computador x <10 uma vez e insira o resultado dessa operação como parâmetro. Portanto, no mundo das vírgulas, você colocaria x <10 se desejar passar o valor de x <0 para uma função.

Aqui você especifica que o programa deve verificar x <10 toda vez que o loop for passado. Então isso é uma instrução.

x ++ é definitivamente outras instruções.

Essas são todas as instruções. Então eles são separados por ponto e vírgula.


Não é uma afirmação. É uma expressão separada por ponto e vírgula. Uma afirmação é completamente diferente.

x <10 pode ser uma expressão (que seria normalmente separados por ponto e vírgula x = 0 é definitivamente uma instrução / instruções..
user4951

Observe o bnf para C - se o loop for fosse uma instrução, alguém poderia usar outras instruções como outra for switchou return dentro da definição do loop for (ie for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) --- você não pode. Não é uma afirmação. Em vez disso, é definido comofor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>

Estranho. int i = 0 é uma expressão correta, mas fazemos isso principalmente para declarar um int, ou seja, i e atribuir 0 a ele (ele também retorna 0, mas como efeito colateral. Você não pode fazer por ({int i = 0; ? j = i}; j <0; cout << "Olá, mundo") pode você Ou sim eu acho que você pode.
user4951

11
@ JimThio: Você provavelmente não está ciente disso, mas "declaração" e "expressão" têm significados muito precisos nos padrões de linguagem. int i = 0definitivamente NÃO é uma expressão. O conjunto de regras que descrevem uma expressão é bastante complexo, considerando o que pode constituir uma expressão, mas a construção TYPE NAME = EXPRESSIONnão corresponde a nenhuma dessas regras.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.