Warum werden Semikolons und Kommas für Schleifen vertauscht?

49

In vielen Sprachen (eine breite Liste von C bis JavaScript):

  • durch Kommas ,getrennte Argumente (zB func(a, b, c)), während
  • Semikolons ;trennen sequentielle Anweisungen (zB instruction1; instruction2; instruction3).

Warum ist diese Zuordnung für for-Schleifen in denselben Sprachen umgekehrt :

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

statt (was mir natürlicher erscheint)

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

?

Sicher, forist ( in der Regel) keine Funktion, sondern Argumente ( das heißt init, condition, increment) verhalten sich eher wie Argumente einer Funktion als eine Folge von Anweisungen.

Liegt es an historischen Gründen / einer Konvention oder gibt es eine gute Begründung für den Austausch von ,und ;in Schleifen?

Piotr Migdal
quelle
1
(Mein erster Beitrag hier. Ich bin mir nicht sicher, ob diese Frage mehr Programmierern oder SO gehört, also zögern Sie nicht zu migrieren, wenn es benötigt wird.)
Piotr Migdal
8
Dies ist definitiv ein Programmierer Beitrag. Herzlich willkommen! :-)
Martijn Pieters
1
"Warum nicht" liefert die Antwort - mit der gleichen Antwort - "Weil jemand eine Wahl treffen musste, und das ist die Wahl, die er getroffen hat" Gleich wie "Warum haben sie" {"und"} "und 1000 andere Entscheidungen getroffen sie machten - "weil"
Mattnz
2
@mattnz Die Frage ist nach der Konsistenz (nicht "Warum verwenden wir ;nicht |?" (oder Warum verwenden wir "sonst" nicht "anders"? )), die nicht für eine Sprache, sondern für eine große Anzahl von ihnen gilt. Eine Antwort, die z. B. "in C als Kurzform für while-Schleife erstellt wurde (und mehrere Anweisungen für inc wurden erst später in Betracht gezogen), und die nicht geändert werden sollte, um Irritationen der Programmierer zu vermeiden", wäre vollkommen in Ordnung.
Piotr Migdal
Ich erinnere mich , zu lesen - vielleicht in in K & R - , dass der Komma - Operator ursprünglich in die Sprache hinzugefügt wurde , um es möglich zu machen mehr als eine Variable in dem init-Ausdruck eines for - Anweisung zu initialisieren.
zwol

Antworten:

18

Warum wird eine solche Zuordnung in denselben Sprachen für for-Schleifen umgekehrt?

Technisch ist das Mapping nicht "umgekehrt".

  • Die durch Kommas getrennten Dinge sind keine Parameter. In (mindestens) C ++ und Java können sie Deklarationen sein, es handelt sich also nicht einmal um Ausdrücke.
  • Die durch Semikolons getrennten Dinge sind ebenfalls keine (einzelnen) Anweisungen.

In Wirklichkeit haben wir hier einen anderen syntaktischen Kontext, in dem dieselben Symbole unterschiedlich verwendet werden. Wir vergleichen nicht Gleiches mit Gleichem, daher gibt es keine Abbildung und kein starkes Argument für eine konsistente Abbildung auf der Grundlage semantischer Konsistenz.

Warum also nicht umgekehrt?

Nun, ich denke, die Gründe dafür liegen in der "natürlichen" Bedeutung von ,und ;. In englischer Sprache ist ein Semikolon "stärker" als ein Komma, und die Glyphe für ein Semikolon ist sichtbarer als ein Komma. Diese beiden Dinge lassen das derzeitige Arrangement (für mich!) Natürlicher erscheinen.

Die einzige Möglichkeit, sicher zu wissen, warum die Syntax gewählt wurde, wäre, wenn die C-Designer uns sagen könnten, was sie im Jahr 1970 gedacht haben. Ich bezweifle, dass sie eine klare Erinnerung an technische Entscheidungen haben, die so weit zurückliegen.


Ist es aus historischen Gründen / eine Konvention

Ich kenne keine Sprache vor C, die eine C-ähnliche Syntax für "for" -Schleifen verwendet hat:

  • Donal Fellows bemerkt, dass BCPL und B kein äquivalentes Konstrukt hatten.

  • Die Äquivalente FORTRAN, COBOL und Algol-60 (und Pascal) waren weniger aussagekräftig und besaßen Syntaxen, die nicht der C-for-Syntax entsprachen.

Aber Sprachen wie C, C ++ und Java, die nach C kamen, leihen eindeutig ihre "for" -Syntax von C.

Stephen C
quelle
Die Philosophie von ( ,vs ;) ist also (schwächer vs stärkerer Bruch), nicht (Tupel vs Sequenzteiler), oder? Noch ist für mich nicht klar, ob Argumente oder Anweisungen stärkere Unterbrechungen benötigen (wie in vielen Fällen für eine Folge von Anweisungen, Unterbrechungen sind implizit (siehe z. B. JavaScript (z. B. i++[line break]j++)), aber zumindest verstehe ich jetzt, warum die aktuelle Konvention ist nicht "offensichtlich umgekehrt".
Piotr Migdal
@PiotrMigdal das Komma als Trennzeichen würde die Verwendung des Kommaoperanden verhindern und könnte bedeuten, dass die Komponenten der for-Schleife eher Anweisungen als Ausdrücke sind. Dies hat erhebliche Auswirkungen.
Der letzte Kommentar hat mich neugierig gemacht, was BCPL getan hat, aber anscheinend war er da FOR i = e1 TO e2 BY e3 DO c(e1..e3-Ausdrücke, c-Befehl), der der BASIC-Syntax ähnlicher ist. Quelle
ein CVn
1
@PiotrMigdal - Die "Philosophie" ist das, woran K & R und die anderen 1970 dachten. Ich glaube nicht, dass es so tief ging, wie Sie es sich vorstellen. (Sie versuchten, eine "höhere Ebene" Sprache zu implementieren, um zu vermeiden, Massen von Telefonvermittlungssoftware in Assembler schreiben zu müssen.)
Stephen C
Ich habe gerade nachgesehen; Die forSyntax wurde in C eingeführt (es war nicht in B oder BCPL).
Donal Fellows
60

Wir schreiben Schleifen wie:

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

Die Sprache könnte so definiert sein, dass Schleifen wie folgt aussehen:

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

Denken Sie jedoch an dieselbe Schleife, die mit einer while-Schleife implementiert wurde:

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

Beachten Sie, dass die x=0und x++sind Aussagen, beendet durch Semikolons. Es sind keine Ausdrücke wie in einem Funktionsaufruf. Semikolons werden zum Trennen von Anweisungen verwendet, und da zwei der drei Elemente in einer for-Schleife Anweisungen sind, wird dies dort verwendet. Eine for-Schleife ist nur eine Abkürzung für eine solche while-Schleife.

Darüber hinaus verhalten sich die Argumente nicht wirklich wie Argumente für eine Funktion. Die zweite und dritte werden wiederholt ausgewertet. Es ist wahr, dass sie keine Sequenz sind, aber sie sind auch keine Funktionsargumente.

Die Tatsache, dass Sie Kommas verwenden können, um mehrere Anweisungen in der for-Schleife zu haben, können Sie auch außerhalb der for-Schleife ausführen.

x = 0, y= 3;

ist eine absolut gültige Aussage auch außerhalb einer for-Schleife. Ich kenne keine praktische Verwendung außerhalb der for-Schleife. Der Punkt ist jedoch, dass Kommas Anweisungen immer unterteilen. Dies ist keine Besonderheit der for-Schleife.

Winston Ewert
quelle
Klar, ich verstehe, dass eine "while" -Schleife "grundlegender" ist. Aber eine solche "Kurznotation" macht (zumindest für mich) nicht viel Sinn, da Sie mit x = 0; y = 0;und (in der geschweiften Klammer) beginnen könnten x++; y++;...
Piotr Migdal
2
@PiotrMigdal, oh du könntest. Mein Punkt ist, dass die Teile in der for-Schleife Anweisungen (die durch Semikolons getrennt sind) und keine Ausdrücke (die durch Kommas getrennt sind) sind
Winston Ewert
1
Ich verstehe den Unterschied, nur für mich ;ist eine Abfolge von Aussagen selbstverständlich , die keine Aussagen trennt (ist es also nur so, dass der Geschmack unterschiedlich ist?). Und in der gegenwärtigen Konvention endet man sowieso gelegentlich damit, Folgen von Anweisungen durch Kommas zu trennen ...
Piotr Migdal
3
@PiotrMigdal, Mitglieder von Strukturen / Gewerkschaften sind durch Semikolons getrennt, aber diese sind nicht wirklich sequentiell. Es ist also sicherlich nicht auf eine Abfolge von Aussagen in seiner Verwendung beschränkt. Am Ende des Tages kommt die Syntax eher auf den Geschmack.
Winston Ewert
Ich kenne jedoch keine praktische Verwendung außerhalb der for-Schleife. Wie wäre es, wenn (foo)?bar++, qux++:bletchder ?:Ausdruck zwei Dinge und nicht nur eine tun soll? Der Rückgabewert fooist true qux, aber beide barund quxwerden inkrementiert.
15

In C und C ++ ist dies der Kommaoperator, nicht nur ein Komma.

Die Grammatik für eine forSchleife ist so etwas wie

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

Im Falle Ihrer Frage:

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

Beachten Sie, dass Sie mit dem Komma-Operator mehrere Aktionen in einer Anweisung ausführen können (wie es der Compiler sieht). Wenn Ihr Vorschlag umgesetzt würde, wäre die Grammatik unklar, wann der Programmierer beabsichtigt, eine Komma-Operator-Anweisung oder ein Trennzeichen zu schreiben.

Kurz gesagt, ;bedeutet das Ende einer Anweisung. Eine forSchleife ist ein Schlüsselwort, gefolgt von einer Liste optionaler Anweisungen, die von umgeben sind (). Die Komma-Operator-Anweisung ermöglicht die Verwendung ,in einer einzelnen Anweisung.

James
quelle
3
Eine for-Schleife ist eine Gruppe von Ausdrücken, die durch Semikolons getrennt sind. Anweisungen können viel mehr als Ausdrücke sein - man kann keine case-Anweisung oder if-Anweisung in einen for-Schleifenabschnitt einfügen. Es ist eine wichtige Implikation zu sagen, dass die Komponenten der for-Schleife Anweisungen sind, wenn man sich die bnf-Form einer for-Schleife
@MichaelT: In C ++ forerlaubt die Syntax einer Schleife explizit eine Anweisung (Deklaration) als ersten Teil. (C ++ erlaubte Deklarationen im Gegensatz zu seinem Vorgänger C89 mitten in der Funktion). Sie können solche Aussagen nicht über mehrere Sprachen hinweg verallgemeinern, auch nicht für zwei Sprachen, die so nah wie C und C ++ sind.
MSalters
@MichaelT Hast du den Teil "ist so was wie" verpasst?
James
@James Sie können das "Etwas wie" vermeiden, indem Sie die tatsächliche BNF von for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>für C und for ( for-init-statement; conditionopt ; expressionopt ) statementfür C ++ verwenden --- Das ';' bedeutet nicht nur einen Anweisungsabschluss. Auf eine for-Schleife folgen keine Anweisungen in ().
8

Es gibt keine konzeptionelle Umkehrung.

Semikolons in C stehen für größere Unterteilungen als Kommas. Sie trennen Aussagen und Erklärungen.

Die Hauptunterteilung in der for-Schleife besteht darin, dass es drei Ausdrücke (oder eine Deklaration und zwei Ausdrücke) und einen Body gibt.

Die Kommas, die Sie in C for-Schleifen sehen, sind nicht Teil der Syntax der for-Schleife. Sie sind nur Manifestationen des Kommaoperators.

Kommas sind wichtige Trennzeichen zwischen Argumenten in Funktionsaufrufen und zwischen Parametern in Funktionsdeklarationen. Semikolons werden jedoch nicht verwendet. Die for-Schleife ist eine spezielle Syntax. es hat nichts mit Funktionen oder Funktionsaufrufen zu tun.

Kaz
quelle
2

Vielleicht ist dies etwas spezielles für C / C ++, aber ich poste diese Antwort, da die Syntax der von Ihnen beschriebenen Sprachen hauptsächlich von der C-Syntax beeinflusst wird.

Abgesehen davon, dass die zuvor beantworteten Fragen aus technischer Sicht zutreffen, liegt dies auch daran, dass in C (und C ++) das Komma tatsächlich ein Operator ist, den Sie sogar überladen können . Die Verwendung eines Semikolon-Operators ( operator;()) würde möglicherweise das Schreiben von Compilern erschweren, da das Semikolon der Terminator für den axiomatischen Ausdruck ist.

Das Interessante daran ist, dass das Komma in der gesamten Sprache als Trennzeichen verwendet wird. Es scheint, als sei der Komma-Operator eine Ausnahme, der hauptsächlich verwendet wird, um for-loops mit mehreren Bedingungen zum Laufen zu bringen. Also, was ist der Deal?

In der Tat das operator,ist das gleiche wie in den Definitionen, Argumentlisten zu tun gebaut, und so weiter: Es hat Build gewesen separates Ausdrücke - etwas , das syntaktische Konstrukt ,nicht tun kann. Sie kann nur das trennen, was in der Norm definiert wurde.

Das Semikolon wird jedoch nicht getrennt - es wird beendet . Und dies führt uns auch zurück zu der ursprünglichen Frage:

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

Das Komma trennt die Ausdrücke in die drei Schleifenteile, während das Semikolon einen Teil (Initialisierung, Bedingung oder nachträglicher Gedanken) der Schleifendefinition beendet.

Neuere Programmiersprachen (wie C #) erlauben möglicherweise keine Überladung des Komma-Operators, aber sie haben höchstwahrscheinlich die Syntax beibehalten, da sich eine Änderung irgendwie unnatürlich anfühlt.

Aschratt
quelle
Es gibt ein Problem mit diesem Argument. In einer forAnweisung wird das ;Symbol einfach als Trennzeichen verwendet. Es trennt die 3 syntaktischen Teile der Anweisung. Es gibt kein drittes Semikolon zum "Beenden" der progressiven Ausdrucksliste. Es wird mit einem anderen Token beendet - ).
Stephen C
0

Für mich sind sie eher weniger bedeutungsähnlich zu ihrem sprachlichen Sinn. Kommas werden bei Listen und Semikolons mit mehreren getrennten Teilen verwendet.

In haben func(a, b, c)wir eine Liste von Argumenten.

instruction1; instruction2; instruction3 ist vielleicht eine Liste, aber eine Liste von separaten und unabhängigen Anweisungen.

Während for ( init1, init2; condition; inc1, inc2 )wir in sind, haben wir drei separate Teile - eine Liste von Initialisierungen, eine Bedingung und eine Liste von inkrementellen Ausdrücken.

ludwika
quelle
0

Der einfachste Weg, es zu sehen, ist der folgende:

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

ist:

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

Mit anderen Worten, das Ding x = 0 ist eigentlich eher eine Anweisung als ein Parameter. Sie fügen dort eine Aussage ein. Daher werden sie durch Semikolon getrennt.

In der Tat gibt es keine Möglichkeit, sie durch Komma zu trennen. Wann fügen Sie das letzte Mal Dinge wie x <10 als Parameter ein? Sie tun dies, wenn Sie x <10 einmal rechnen und das Ergebnis dieser Operation als Parameter einfügen möchten. In der Kommawelt würden Sie also x <10 setzen, wenn Sie den Wert von x <0 an eine Funktion weitergeben möchten.

Hier legen Sie fest, dass das Programm bei jedem Durchlauf der Schleife x <10 prüfen soll. Das ist also eine Anweisung.

x ++ ist definitiv eine andere Anleitung.

Das sind alles Anweisungen. Sie sind also durch ein Semikolon getrennt.

user4951
quelle
Es ist keine Aussage. Es ist ein durch ein Semikolon getrennter Ausdruck. Eine Aussage ist ganz anders.
x <10 kann ein Ausdruck sein (der normalerweise durch ein Semikolon getrennt wird. x = 0 ist definitiv eine Anweisung / Anweisung.
user4951
Schauen Sie sich das bnf für C an - wenn die for-Schleife Anweisungen war, könnte man andere Anweisungen wie eine andere for switchoder return innerhalb der for-Schleifendefinition (dh for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) verwenden - das ist nicht möglich. Es ist keine Aussage. Stattdessen ist es definiert alsfor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>
Seltsam. int i = 0 ist ein Ausdruck, aber wir tun es hauptsächlich, um ein int, nämlich i, zu deklarieren und ihm 0 zuzuweisen (es gibt auch 0 zurück, aber als Nebeneffekt. Das können Sie nicht tun für ({int i = 0; j = i}; j <0; cout << "Hallo Welt") können Sie? Oder ja, ich denke, Sie können.
user4951
1
@ JimThio: Sie wissen es wahrscheinlich nicht, aber "Aussage" und "Ausdruck" haben sehr genaue Bedeutungen in Sprachstandards. int i = 0ist definitiv kein Ausdruck. Der Regelsatz, der einen Ausdruck beschreibt, ist ziemlich komplex, wenn man bedenkt, was einen Ausdruck ausmachen kann, aber das Konstrukt TYPE NAME = EXPRESSIONstimmt mit keiner dieser Regeln überein.
MSalters