Perché i punti e virgola e le virgole sono scambiati per i loop?


49

In molte lingue (un ampio elenco, da C a JavaScript):

  • virgole ,separate argomenti (ad es. func(a, b, c)), mentre
  • i punti e virgola ;separano le istruzioni sequenziali (ad es instruction1; instruction2; instruction3.).

Quindi perché questa mappatura è invertita nelle stesse lingue per i loop :

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

invece di (ciò che mi sembra più naturale)

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

?

Certo, forè (di solito) non è una funzione, ma gli argomenti (ad esempio init, condition, increment) si comportano più come argomenti di una funzione di una sequenza di istruzioni.

È dovuto a ragioni storiche / a una convenzione o esiste una buona logica per lo scambio di ,e ;nei cicli?


1
(Il mio primo post qui. Non sono sicuro che questa domanda appartenga più ai programmatori o ai SO, quindi sentitevi liberi di migrare, se necessario.)
Piotr Migdal

8
Questo è sicuramente un post programmatori. Benvenuto! :-)
Martijn Pieters,

1
"Why Not" fornirà la risposta - avendo la stessa risposta - "Perché qualcuno doveva fare una scelta, e questa è la scelta che hanno fatto" Come per "Perché hanno scelto" {"e"} "e altre 1000 scelte hanno fatto - "Perché".
mattnz

2
@mattnz La domanda riguarda la consistenza (non "Perché usiamo ;no |?" (o Perché usiamo 'altra cosa' non 'altro'? )), che non è caso per una lingua, ma un gran numero di essi. Una risposta che, ad esempio, "è stata fatta in C come abbreviazione di while loop (e più dichiarazioni per inc sono state pensate solo in seguito), e la gente non ha voluto cambiarlo per evitare l'irritazione dei programmatori" sarebbe perfettamente soddisfacente.
Piotr Migdal,

Ricordo di aver letto - forse in K&R - che l'operatore virgola era stato originariamente aggiunto alla lingua per rendere possibile l'inizializzazione di più di una variabile nell'espressione init di un'istruzione for.
zwol,

Risposte:


18

Quindi perché nelle stesse lingue tale mappatura è invertita per i loop.

Tecnicamente, la mappatura non è "invertita".

  • Le cose separate da virgole non sono parametri. In (almeno) C ++ e Java, possono essere dichiarazioni, quindi non sono nemmeno espressioni.
  • Le cose separate da punti e virgola non sono neanche (singole) dichiarazioni.

In realtà quello che abbiamo qui è un diverso contesto sintattico in cui gli stessi simboli vengono usati in modo diverso. Non stiamo confrontando il like con il like, quindi non esiste un mapping e nessun argomento forte per un mapping coerente basato sulla coerenza semantica.

Quindi perché non farlo al contrario?

Beh, penso che le ragioni derivino dal significato "naturale" di ,e ;. Nella lingua scritta inglese, un punto e virgola è una pausa "più forte" di una virgola e il glifo per punto e virgola è più visibile di una virgola. Queste due cose si combinano per rendere la disposizione attuale (per me!) Più naturale.

Ma l'unico modo per sapere con certezza perché è stata fatta la scelta della sintassi sarebbe se i progettisti del C potessero dirci cosa stavano pensando nel 1970. Dubito che abbiano un chiaro ricordo delle decisioni tecniche prese così indietro nel tempo.


È per motivi storici / una convenzione

Non sono a conoscenza di alcun linguaggio prima di C che utilizzava una sintassi simile a C per i cicli "for":

  • Donal Fellows osserva che BCPL e B non avevano un costrutto equivalente.

  • Gli equivalenti FORTRAN, COBOL e Algol-60 (e Pascal) erano meno espressivi e avevano sintassi che non assomigliavano alla sintassi "C".

Ma linguaggi come C, C ++ e Java che sono venuti dopo C hanno chiaramente preso in prestito la loro sintassi "for" da C.


Quindi, la filosofia di ( ,vs ;) è (rottura più debole vs più forte), piuttosto che (tupla- vs sequenza-splitter), giusto? Tuttavia, per me non è ovvio se argomenti o affermazioni necessitino di interruzioni più forti (come in molti casi per una sequenza di affermazioni, le interruzioni sono implicite (vedi ad esempio JavaScript (ad esempio i++[line break]j++))), ma almeno ora capisco perché l'attuale convenzione non è "ovviamente invertito".
Piotr Migdal,

@PiotrMigdal la virgola come delimitatore impedirebbe l'uso dell'operando virgola e potrebbe implicare che i componenti del ciclo for siano istruzioni anziché espressioni. Ciò ha implicazioni significative.

L'ultimo commento mi ha incuriosito quello che ha fatto BCPL, ma apparentemente era lì FOR i = e1 TO e2 BY e3 DO c(espressioni e1..e3, comando c), che assomiglia più da vicino alla sintassi di BASIC. Fonte
un CVn

1
@PiotrMigdal - La "filosofia" è qualunque cosa K&R e gli altri stessero ripensando nel 1970. Non penso che sia andato nella profondità del pensare che immagini. (Stavano cercando di implementare un linguaggio di "livello superiore" per evitare di dover scrivere masse di software di commutazione telefonica nell'assemblatore.)
Stephen C

Ho appena controllato; la forsintassi è stata introdotta in C (non era in B o BCPL).
Donal Fellows il

60

Scriviamo loop come:

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

La lingua avrebbe potuto essere definita in modo tale che i loop fossero:

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

Tuttavia, pensa allo stesso ciclo implementato usando un ciclo while:

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

Si noti che le dichiarazioni x=0e x++sono, terminate da punti e virgola. Non sono espressioni come quelle che avresti in una chiamata di funzione. I punti e virgola vengono utilizzati per separare le istruzioni e poiché due dei tre elementi in un ciclo for sono istruzioni, questo è ciò che viene utilizzato lì. Un ciclo for è solo una scorciatoia per un ciclo while.

Inoltre, gli argomenti non agiscono come argomenti per una funzione. Il secondo e il terzo vengono ripetutamente valutati. È vero che non sono una sequenza, ma non sono nemmeno argomenti di funzione.

Inoltre, il fatto che sia possibile utilizzare le virgole per avere più istruzioni nel ciclo for è in realtà qualcosa che si può fare al di fuori del ciclo for.

x = 0, y= 3;

è un'affermazione perfettamente valida anche al di fuori di un ciclo for. Tuttavia, non conosco alcun uso pratico al di fuori del ciclo for. Ma il punto è che le virgole suddividono sempre le dichiarazioni; non è una caratteristica speciale del ciclo for.


Certo, capisco che il ciclo "while" è "più fondamentale". Ma tale "notazione abbreviata" non ha molto senso (almeno per me), dal momento che potresti iniziare con x = 0; y = 0;(all'interno della parentesi graffa) x++; y++;...
Piotr Migdal

2
@PiotrMigdal, oh potresti. Il mio punto è che i pezzi all'interno del ciclo for sono dichiarazioni (che sono separate da punti e virgola) non espressioni (che sono separate da virgole)
Winston Ewert,

1
Ottengo la differenza, solo per me ;è naturale per una sequenza di affermazioni , non necessariamente separando alcuna affermazione (quindi è solo che i gusti differiscono?). E nell'attuale convenzione si finisce occasionalmente con separare sequenze di dichiarazioni con virgole comunque ...
Piotr Migdal

3
@PiotrMigdal, i membri di strutture / unioni sono separati da punti e virgola, ma quelli non sono realmente sequenziali. Quindi non è certamente limitato a una sequenza di affermazioni nel suo uso. Alla fine della giornata, la sintassi preferisce scendere a piacere.
Winston Ewert,

Non conosco alcun uso pratico al di fuori del ciclo for però --- Che ne dici di (foo)?bar++, qux++:bletch--- Dove vuoi che l' ?:espressione faccia due cose piuttosto che una sola. Il valore restituito se fooè vero è qux, ma entrambi bare quxvengono incrementati.

15

In C e C ++ questo è l'operatore virgola, non solo una virgola.

La grammatica per un forciclo è qualcosa di simile

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

Nel caso della tua domanda:

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

Si noti che l'operatore virgola consente di eseguire più azioni in un'unica istruzione (come viene visto dal compilatore). Se il tuo suggerimento fosse implementato, ci sarebbe un'ambiguità nella grammatica su quando il programmatore intendeva scrivere un'istruzione virgola o un separatore.

In breve, ;indica la fine di un'istruzione. Un forciclo è una parola chiave seguita da un elenco di istruzioni facoltative racchiuse tra (). L'istruzione virgola-operatore consente l'uso di ,in una singola istruzione.


3
Un ciclo for è un insieme di espressioni separate da punti e virgola. Le istruzioni possono essere molto più che espressioni: non è possibile inserire un'istruzione case o if nell'istruzione for loop. È un'implicazione significativa affermare che i componenti del ciclo for sono istruzioni quando si osserva la forma bnf di un ciclo for

@MichaelT: Ma in C ++ la sintassi di un forciclo consente esplicitamente un'istruzione (dichiarazione) come prima parte. (Il C ++ ha consentito dichiarazioni a metà funzione, al contrario del suo predecessore C89). Non è possibile generalizzare tali dichiarazioni tra le lingue, anche per 2 lingue vicine a quelle di C e C ++.
MSalters il

@MichaelT Ti sei perso la parte 'è qualcosa di simile'?
James,

@James puoi evitare il "qualcosa di simile" usando il bnf effettivo di for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>per C e for ( for-init-statement; conditionopt ; expressionopt ) statementper C ++ --- Il ';' non significa solo un terminatore di istruzioni. Un ciclo for non è seguito da istruzioni racchiuse tra ().

8

Non c'è inversione concettuale.

I punti e virgola in C rappresentano più divisioni maggiori delle virgole. Separano dichiarazioni e dichiarazioni.

Le divisioni principali nel ciclo for sono che ci sono tre espressioni (o una dichiarazione e due espressioni) e un corpo.

Le virgole che vedi in C per i loop non fanno specificamente parte della sintassi del ciclo for. Sono solo manifestazioni dell'operatore virgola.

Le virgole sono i principali separatori tra gli argomenti nelle chiamate di funzione e tra i parametri nelle dichiarazioni di funzione, ma i punti e virgola non vengono utilizzati. Il ciclo for è una sintassi speciale; non ha nulla a che fare con le funzioni o le chiamate di funzione.


2

Forse questo è qualcosa di specifico per C / C ++, ma io inserisco questa risposta, perché la sintassi dei linguaggi che hai descritto è principalmente influenzata dalla sintassi C.

Oltre alle domande precedentemente risposte sono vere, da un punto di vista tecnico, anche perché in C (e C ++) la virgola è in realtà un operatore , che puoi persino sovraccaricare . L'uso di un punto e virgola ( operator;()) renderebbe più difficile la scrittura di compilatori, poiché il punto e virgola è il terminatore di espressioni assiomatiche.

Ciò che rende questo interessante è il fatto che la virgola è ampiamente usata come separatore in tutta la lingua. Sembra che l'operatore virgola sia un'eccezione, utilizzata principalmente per far funzionare i forloop con più condizioni, quindi qual è il problema?

In effetti operator,è stato creato per fare la stessa cosa come nelle definizioni, negli elenchi di argomenti e così via: è stato costruito per espressioni separate - qualcosa che il costrutto sintattico ,non può fare. Può solo separare ciò che è stato definito nella norma.

Tuttavia il punto e virgola non si separa - termina . E questo è anche ciò che ci riporta alla domanda originale:

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

La virgola separa le espressioni nelle tre parti del ciclo, mentre il punto e virgola termina una parte (inizializzazione, condizione o ripensamento) della definizione del ciclo.

I linguaggi di programmazione più recenti (come C #) potrebbero non consentire di sovraccaricare l'operatore virgola, ma molto probabilmente hanno mantenuto la sintassi, perché modificarla sembra in qualche modo innaturale.


C'è un problema con questo argomento. In una fordichiarazione il ;simbolo è chiaramente usato come separatore. Separa le 3 parti sintattiche dell'istruzione. Non esiste un terzo punto e virgola per "terminare" l'elenco delle espressioni progressive. È terminato da un token diverso - ).
Stephen C,

0

Per me sono usati significati più meno simili al loro senso linguistico. Le virgole vengono utilizzate con elenchi e punti e virgola con più parti separate.

In func(a, b, c)abbiamo un elenco di argomenti.

instruction1; instruction2; instruction3 è forse un elenco ma un elenco di istruzioni separate e indipendenti.

Mentre in for ( init1, init2; condition; inc1, inc2 )abbiamo tre parti separate: un elenco di inizializzazioni, una condizione e un elenco di espressioni di incremento.


0

Il modo più semplice per vederlo è il seguente:

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

è:

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

In altre parole, quella cosa x = 0 è in realtà un'istruzione / istruzione piuttosto che un parametro. Inserisci una dichiarazione lì. Quindi sono separati da punto e virgola.

In effetti non è possibile separarli con una virgola. Quando viene inserita l'ultima volta cose come x <10 come parametro? Lo fai se vuoi computer x <10 una volta e inserisci il risultato di quell'operazione come parametro. Quindi nel mondo delle virgole inseriresti x <10 se vuoi passare il valore di x <0 a una funzione.

Qui si specifica che il programma dovrebbe controllare x <10 ogni volta che si passa il ciclo. Quindi questa è un'istruzione.

x ++ è sicuramente un'altra istruzione.

Queste sono tutte le istruzioni. Quindi sono separati da punti e virgola.


Non è un'affermazione. È un'espressione separata da un punto e virgola. Una dichiarazione è completamente diversa.

x <10 può essere un'espressione (che di solito sarebbe separata da un punto e virgola. x = 0 è sicuramente un'istruzione / istruzioni.
user4951

Guarda il bnf per C - se il ciclo for era istruzioni, si potrebbero usare altre istruzioni come un'altra for switcho return all'interno della definizione del ciclo for (cioè for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) --- non puoi. Non è un'affermazione. Invece è definito comefor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>

Strano. int i = 0 è un'espressione tutto ok, ma lo facciamo principalmente per dichiarare un int e cioè i e assegnargli 0 (restituisce anche 0 ma come effetto collaterale. Non puoi farlo per ({int i = 0; j = i}; j <0; cout << "Ciao mondo") puoi? O sì, penso di si.
user4951

1
@JimThio: Probabilmente non ne sei consapevole, ma "istruzione" ed "espressione" hanno significati molto precisi negli standard linguistici. int i = 0sicuramente NON è un'espressione. L'insieme di regole che descrivono un'espressione è abbastanza complesso, considerando ciò che può costituire un'espressione, ma il costrutto TYPE NAME = EXPRESSIONnon corrisponde a nessuna di quelle regole.
MSalters il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.