Warum verwenden Programmiersprachen, insbesondere C, geschweifte Klammern und keine quadratischen?

96

Die Definition von "C-Style-Sprache" kann praktisch vereinfacht werden, indem "geschweifte Klammern ( {}) verwendet werden". Warum verwenden wir dieses bestimmte Zeichen (und warum nicht etwas Vernünftigeres, []das die Umschalttaste zumindest auf US-Tastaturen nicht erfordert)?

Gibt es einen tatsächlichen Vorteil für die Programmiererproduktivität, der sich aus diesen Klammern ergibt, oder sollten neue Sprachdesigner nach Alternativen suchen (dh nach den Leuten, die hinter Python stehen)?

Wikipedia sagt uns , dass C Verwendungen sagte Zahnspange, aber nicht , warum. Eine Aussage im Wikipedia-Artikel über die Liste der C-basierten Programmiersprachen legt nahe, dass dieses Syntaxelement etwas Besonderes ist:

Allgemein gesprochen sind Sprachen der C-Familie solche, die eine C-ähnliche Block-Syntax verwenden (einschließlich geschweifter Klammern , um den Block zu beginnen und zu beenden) ...

SomeKittens
quelle
35
Die einzige Person, die darauf antworten kann, ist Dennis Ritchie und er ist tot. Eine vernünftige Vermutung ist, dass [] bereits für Arrays verwendet wurden.
Dirk Holsopple
2
@DirkHolsopple Also hat er keine Argumente hinterlassen? Drat. Außerdem: zwei Abstimmungen über etwas, worauf ich wirklich neugierig bin? Danke Jungs ....
SomeKittens
1
Bitte setzen Sie die Diskussion über diese Frage in dieser Meta-Frage fort .
Thomas Owens
2
Ich habe diesen Beitrag freigeschaltet. Bitte bewahren Sie alle Kommentare zu der Frage und die Diskussion über die Angemessenheit der Meta-Frage auf .
Thomas Owens
5
Es hat wahrscheinlich auch etwas mit der Tatsache zu tun, dass geschweifte Klammern in der Satznotation in der Mathematik verwendet werden, was sie für den Zugriff auf Arrayelemente etwas umständlich macht, anstatt Dinge wie das Deklarieren "satzartiger" Dinge wie Strukturen, Arrays usw. Sogar moderne Sprachen wie Python verwenden geschweifte Klammern, um Mengen und Wörterbücher zu deklarieren. Die Frage ist also, warum C auch geschweifte Klammern verwendete, um den Geltungsbereich zu deklarieren. Wahrscheinlich, weil den Designern die bekannten Alternativen wie BEGIN / END einfach nicht gefielen und das Überladen der Array-Zugriffsnotation ([]) als weniger ästhetisch als die Set-Notation angesehen wurde.
Charles Salvia

Antworten:

102

Zwei der Haupteinflüsse auf C waren die Algol-Sprachfamilie (Algol 60 und Algol 68) und BCPL (von denen C seinen Namen hat).

BCPL war die erste Programmiersprache für geschweifte Klammern, und die geschweiften Klammern überlebten die syntaktischen Änderungen und wurden zu einem gängigen Mittel zur Bezeichnung von Programmquellcodeanweisungen. In der Praxis verwendeten Quellprogramme auf begrenzten Tastaturen des Tages häufig die Sequenzen $ (und $) anstelle der Symbole {und}. Die einzeiligen // -Kommentare von BCPL, die nicht in C aufgenommen wurden, wurden in C ++ und später in C99 erneut angezeigt.

Von http://www.princeton.edu/~achaney/tmve/wiki100k/docs/BCPL.html

BCPL führte einige Neuerungen ein und implementierte sie, die bei der Gestaltung späterer Sprachen häufig vorkamen. Somit war es die erste Programmiersprache für geschweifte Klammern (eine, die {} als Blocktrennzeichen verwendet), und es war die erste Sprache, die // zum Markieren von Inline-Kommentaren verwendete.

Von http://progopedia.com/language/bcpl/

In BCPL sieht man oft geschweifte Klammern, aber nicht immer. Dies war eine Einschränkung der Tastaturen zu der Zeit. Die Zeichen $(und $)waren lexikographisch äquivalent zu {und }. Digraphen und Trigraphen wurden in C beibehalten (obwohl ein anderes Set für das Ersetzen von geschweiften Klammern - ??<und ??>).

Die Verwendung von geschweiften Klammern wurde in B (das C vorausging) weiter verfeinert .

Aus der Benutzerreferenz zu B von Ken Thompson:

/* The following function will print a non-negative number, n, to
  the base b, where 2<=b<=10,  This routine uses the fact that
  in the ASCII character set, the digits 0 to 9 have sequential
  code values.  */

printn(n,b) {
        extern putchar;
        auto a;

        if(a=n/b) /* assignment, not test for equality */
                printn(a, b); /* recursive */
        putchar(n%b + '0');
}

Es gibt Hinweise darauf, dass geschweifte Klammern als Kurzschrift für beginund endinnerhalb von Algol verwendet wurden.

Ich erinnere mich, dass Sie sie auch in den 256-stelligen Kartencode aufgenommen haben, den Sie in CACM veröffentlicht haben, weil ich es interessant fand, dass Sie vorgeschlagen haben, sie anstelle der Algol-Schlüsselwörter "begin" und "end" zu verwenden wie sie später in der C-Sprache verwendet wurden.

Von http://www.bobbemer.com/BRACES.HTM


Die Verwendung von eckigen Klammern (als vorgeschlagener Ersatz in der Frage) geht noch weiter zurück. Wie bereits erwähnt, beeinflusste die Algol-Familie C. Innerhalb von Algol 60 und 68 (C wurde 1972 und BCPL 1966 geschrieben) wurde die eckige Klammer verwendet, um einen Index in einem Array oder einer Matrix zu kennzeichnen.

BEGIN
  FILE F(KIND=REMOTE);
  EBCDIC ARRAY E[0:11];
  REPLACE E BY "HELLO WORLD!";
  WRITE(F, *, E);
END.

Da Programmierer bereits mit eckigen Klammern für Arrays in Algol und BCPL und geschweiften Klammern für Blöcke in BCPL vertraut waren, bestand kaum Bedarf oder Wunsch, dies zu ändern, wenn eine andere Sprache erstellt wurde.


Die aktualisierte Frage enthält einen Produktivitätszusatz für die Verwendung von geschweiften Klammern und erwähnt Python. Es gibt einige andere Ressourcen, die diese Studie durchführen, obwohl die Antwort auf "Es ist anekdotisch, und was Sie gewohnt sind, ist, was Sie am produktivsten sind." Aufgrund der sehr unterschiedlichen Programmierkenntnisse und der Vertrautheit mit verschiedenen Sprachen wird es schwierig, diese zu erklären.

Siehe auch: Stapelüberlauf Gibt es statistische Studien, die belegen, dass Python „produktiver“ ist?

Ein Großteil der Gewinne hängt von der verwendeten IDE ab (oder von deren Fehlen). In vi-basierten Editoren wird der Cursor durch %Bewegen des Cursors über ein übereinstimmendes Öffnen / Schließen und Drücken von zum anderen übereinstimmenden Zeichen bewegt. Dies ist mit C-basierten Sprachen früher sehr effizient - heute noch weniger.

Ein besserer Vergleich wäre zwischen {}und begin/ endwelches die Optionen des Tages waren (horizontaler Raum war kostbar). Viele Wirth-Sprachen basierten auf a beginund endstyle (Algol (oben erwähnt), Pascal (viele sind damit vertraut) und der Modula-Familie).

Ich habe Schwierigkeiten, eine zu finden, die dieses spezifische Sprachmerkmal isoliert - bestenfalls kann ich zeigen, dass die geschweiften Klammersprachen viel beliebter sind als die Anfangs- und Endsprachen, und es ist ein allgemeines Konstrukt. Wie oben im Bob Bemer-Link erwähnt, wurde die geschweifte Klammer verwendet, um das Programmieren als Kurzschrift zu vereinfachen.

Von Warum Pascal nicht meine bevorzugte Programmiersprache ist

C- und Ratfor-Programmierer empfinden 'begin' und 'end' im Vergleich zu {und} als sperrig.

Was über alles gesagt werden kann - seine Vertrautheit und Vorliebe.

Gemeinschaft
quelle
14
Jetzt lernt jeder hier BCPL, anstatt zu arbeiten :)
Denys Séguret
Die Trigraphen (eingeführt in der ISO C-Norm von 1989) für {und }sind ??<und ??>. Die mit der Änderung von 1995 eingeführten Digraphen sind <%und %>. Trigraphen werden in einer sehr frühen Übersetzungsphase in allen Kontexten erweitert. Digraphen sind Token und werden nicht in Zeichenfolgenliteralen, Zeichenkonstanten oder Kommentaren erweitert.
Keith Thompson
Vor 1989 gab es dafür in C etwas (ich müsste mein Erstausgabebuch ausgraben, um einen Termin dafür zu bekommen). Nicht alle EBCDIC-Codepages enthielten eine geschweifte Klammer (oder eckige Klammern), und in den frühesten C-Compilern war dies vorgesehen.
@NevilleDNZ BCPL verwendete 1966 geschweifte Zahnspangen. Woher Algol68 seine Vorstellung hat, wäre etwas zu erkunden - aber BCPL hat es nicht von Algo68 bekommen. Der ternäre Operator hat mich interessiert und geht auf CPL (1963) (den Vorgänger von BCPL) zurück, der den Begriff von Lisp (1958) entlehnt hat.
1968: Algol68 erlaubt Runde Klammern (~) als eine Abkürzung von beginnen ~ Ende fett Symbolblocks. Diese werden kurze Symbole genannt, vgl. Wp: Algol68 Fettgedruckte Symbole , mit denen Codeblöcke wie Ausdrücke behandelt werden können . A68 hat auch kurze Abkürzungen wie C's ?: Ternäre Operatoren, zB x:=(c|s1|s2)anstelle von C's x=c?s1|s2. Ähnliches gilt für if & case- Anweisungen. ¢ BTW: A68 ist , von wo aus die Schale bekam es esac & fi ¢
NevilleDNZ
24

Die Eingabe von eckigen Klammern []ist einfacher, seit dem IBM 2741- Terminal, das unter Multics- Betriebssystemen "weit verbreitet" war und für das Dennis Ritchie, einer der Entwickler der Programmiersprache C, Mitglied des Entwicklerteams war .

http://upload.wikimedia.org/wikipedia/commons/thumb/9/9f/APL-keybd2.svg/600px-APL-keybd2.svg.png

Beachten Sie das Fehlen von geschweiften Klammern im IBM 2741-Layout!

In C werden eckige Klammern "genommen", da diese für Arrays und Zeiger verwendet werden . Wenn Sprachentwickler erwarten würden, dass Arrays und Zeiger wichtiger / häufiger verwendet werden als Codeblöcke (was wie eine vernünftige Annahme neben ihnen klingt , mehr im historischen Kontext des Codierungsstils unten), würde dies bedeuten, dass geschweifte Klammern "weniger wichtig" werden " Syntax.

Die Bedeutung von Arrays wird in dem Artikel Die Entwicklung der C-Sprache von Ritchie deutlich. Es gibt sogar eine explizit angegebene Annahme der "Prävalenz von Zeigern in C-Programmen" .

... Neue Sprache behielt eine kohärente und praktikable (wenn auch ungewöhnliche) Erklärung der Semantik von Arrays ... Zwei Ideen sind für C unter den Sprachen seiner Klasse am charakteristischsten : die Beziehung zwischen Arrays und Zeigern ... Das andere charakteristische Merkmal von C, seine Behandlung von Arrays ... hat echte Tugenden . Obwohl die Beziehung zwischen Zeigern und Arrays ungewöhnlich ist, kann sie gelernt werden. Darüber hinaus zeigt die Sprache beträchtliche Macht wichtige Konzepte zu beschreiben, beispielsweise Vektoren , deren Länge variiert während der Laufzeit, mit nur ein paar grundlegenden Regeln und Konventionen ...


Für ein besseres Verständnis des historischen Kontexts und des Codierungsstils der Zeit, als die C-Sprache erstellt wurde, muss berücksichtigt werden, dass "der Ursprung von C eng mit der Entwicklung von Unix verknüpft ist " und insbesondere, dass das Betriebssystem auf einen PDP portiert wird. 11 "führte zur Entwicklung einer frühen Version von C" ( zitiert Quelle ). Laut Wikipedia , "im Jahr 1972 wurde Unix in der Programmiersprache C neu geschrieben" .

Der Quellcode verschiedener alter Versionen von Unix ist online verfügbar, z. B. auf der Unix Tree- Site. Von den verschiedenen dort vorgestellten Versionen scheint die zweite Ausgabe von Unix vom 1972-06 die relevanteste zu sein :

Die zweite Ausgabe von Unix wurde von Ken Thompson, Dennis Ritchie und anderen für den PDP-11 bei Bell Labs entwickelt. Es erweiterte die First Edition mit mehr Systemaufrufen und mehr Befehlen. In dieser Ausgabe begann auch die C-Sprache, mit der einige Befehle geschrieben wurden ...

Sie können C-Quellcode auf der Second Edition Unix (V2) -Seite durchsuchen und studieren , um eine Vorstellung vom typischen Codierungsstil der Zeit zu erhalten.

Ein prominentes Beispiel, das die Idee unterstützt, dass es für Programmierer damals ziemlich wichtig war, eckige Klammern mühelos einzugeben, findet sich im Quellcode von V2 / c / ncc.c :

/* C command */

main(argc, argv)
char argv[][]; {
    extern callsys, printf, unlink, link, nodup;
    extern getsuf, setsuf, copy;
    extern tsp;
    extern tmp0, tmp1, tmp2, tmp3;
    char tmp0[], tmp1[], tmp2[], tmp3[];
    char glotch[100][], clist[50][], llist[50][], ts[500];
    char tsp[], av[50][], t[];
    auto nc, nl, cflag, i, j, c;

    tmp0 = tmp1 = tmp2 = tmp3 = "//";
    tsp = ts;
    i = nc = nl = cflag = 0;
    while(++i < argc) {
        if(*argv[i] == '-' & argv[i][1]=='c')
            cflag++;
        else {
            t = copy(argv[i]);
            if((c=getsuf(t))=='c') {
                clist[nc++] = t;
                llist[nl++] = setsuf(copy(t));
            } else {
            if (nodup(llist, t))
                llist[nl++] = t;
            }
        }
    }
    if(nc==0)
        goto nocom;
    tmp0 = copy("/tmp/ctm0a");
    while((c=open(tmp0, 0))>=0) {
        close(c);
        tmp0[9]++;
    }
    while((creat(tmp0, 012))<0)
        tmp0[9]++;
    intr(delfil);
    (tmp1 = copy(tmp0))[8] = '1';
    (tmp2 = copy(tmp0))[8] = '2';
    (tmp3 = copy(tmp0))[8] = '3';
    i = 0;
    while(i<nc) {
        if (nc>1)
            printf("%s:\n", clist[i]);
        av[0] = "c0";
        av[1] = clist[i];
        av[2] = tmp1;
        av[3] = tmp2;
        av[4] = 0;
        if (callsys("/usr/lib/c0", av)) {
            cflag++;
            goto loop;
        }
        av[0] = "c1";
        av[1] = tmp1;
        av[2] = tmp2;
        av[3] = tmp3;
        av[4] = 0;
        if(callsys("/usr/lib/c1", av)) {
            cflag++;
            goto loop;
        }
        av[0] = "as";
        av[1] = "-";
        av[2] = tmp3;
        av[3] = 0;
        callsys("/bin/as", av);
        t = setsuf(clist[i]);
        unlink(t);
        if(link("a.out", t) | unlink("a.out")) {
            printf("move failed: %s\n", t);
            cflag++;
        }
loop:;
        i++;
    }
nocom:
    if (cflag==0 & nl!=0) {
        i = 0;
        av[0] = "ld";
        av[1] = "/usr/lib/crt0.o";
        j = 2;
        while(i<nl)
            av[j++] = llist[i++];
        av[j++] = "-lc";
        av[j++] = "-l";
        av[j++] = 0;
        callsys("/bin/ld", av);
    }
delfil:
    dexit();
}
dexit()
{
    extern tmp0, tmp1, tmp2, tmp3;

    unlink(tmp1);
    unlink(tmp2);
    unlink(tmp3);
    unlink(tmp0);
    exit();
}

getsuf(s)
char s[];
{
    extern exit, printf;
    auto c;
    char t, os[];

    c = 0;
    os = s;
    while(t = *s++)
        if (t=='/')
            c = 0;
        else
            c++;
    s =- 3;
    if (c<=8 & c>2 & *s++=='.' & *s=='c')
        return('c');
    return(0);
}

setsuf(s)
char s[];
{
    char os[];

    os = s;
    while(*s++);
    s[-2] = 'o';
    return(os);
}

callsys(f, v)
char f[], v[][]; {

    extern fork, execv, wait, printf;
    auto t, status;

    if ((t=fork())==0) {
        execv(f, v);
        printf("Can't find %s\n", f);
        exit(1);
    } else
        if (t == -1) {
            printf("Try again\n");
            return(1);
        }
    while(t!=wait(&status));
    if ((t=(status&0377)) != 0) {
        if (t!=9)       /* interrupt */
            printf("Fatal error in %s\n", f);
        dexit();
    }
    return((status>>8) & 0377);
}

copy(s)
char s[]; {
    extern tsp;
    char tsp[], otsp[];

    otsp = tsp;
    while(*tsp++ = *s++);
    return(otsp);
}

nodup(l, s)
char l[][], s[]; {

    char t[], os[], c;

    os = s;
    while(t = *l++) {
        s = os;
        while(c = *s++)
            if (c != *t++) goto ll;
        if (*t++ == '\0') return (0);
ll:;
    }
    return(1);
}

tsp;
tmp0;
tmp1;
tmp2;
tmp3;

Es ist interessant festzustellen, dass die pragmatische Motivation, Zeichen auszuwählen, um sprachliche Syntaxelemente zu kennzeichnen, die auf ihrer Verwendung in gezielten praktischen Anwendungen basieren, dem Zipfschen Gesetz ähnelt, wie in dieser hervorragenden Antwort erläutert ...

Die beobachtete Beziehung zwischen Frequenz und Länge wird als Zipf-Gesetz bezeichnet

... mit dem einzigen Unterschied, dass die Länge in der obigen Anweisung durch / generalisiert als Schreibgeschwindigkeit ersetzt wird.

Mücke
quelle
5
Irgendetwas, das diese "offensichtliche" Erwartung der Sprachdesigner stützt? In C ist nicht viel Programmierung erforderlich, um festzustellen, dass geschweifte Klammern viel häufiger vorkommen als Array-Deklarationen. Daran hat sich seit jeher nicht viel geändert - schauen Sie sich K & R an.
1
Irgendwie bezweifle ich diese Erklärung. Wir wissen nicht, was erwartet wird, und sie hätten es leicht andersherum auswählen können, da sie die Leute waren, die auch über die Array-Notation entschieden haben. Wir wissen nicht einmal, ob sie geschweifte Klammern für die "weniger wichtige" Option hielten, vielleicht mochten sie geschweifte Klammern mehr.
thorsten müller
3
@gnat: Eckige Klammern sind auf modernen Tastaturen einfacher einzugeben. Gilt dies für die Tastaturen, die es gab, als Unix und C zum ersten Mal implementiert wurden? Ich habe keinen Grund zu der Annahme, dass sie dieselbe Tastatur verwenden oder dass sie davon ausgehen würden, dass andere Tastaturen ihren Tastaturen gleichen oder dass sie gedacht hätten, dass die Schreibgeschwindigkeit es wert wäre, um ein Zeichen optimiert zu werden.
Michael Shaw
1
Das Zipf-Gesetz ist auch eine Verallgemeinerung dessen, was in natürlichen Sprachen geschieht. C wurde künstlich konstruiert, daher gibt es keinen Grund zu der Annahme, dass dies hier zutreffen würde, es sei denn, die Designer von C haben sich bewusst dafür entschieden, es absichtlich anzuwenden. Wenn es zutrifft, gibt es keinen Grund anzunehmen, dass es etwas vereinfacht, das bereits so kurz ist wie ein einzelnes Zeichen.
Michael Shaw
1
@gnat FWIW, grep -Fosagt mir die *.cDateien des CPython-Quellcodes (Rev. 4b42d7f288c5, weil ich sie zur Hand habe), einschließlich libffi, enthält 39511 {(39508 {, keine Ahnung , warum zwei Klammern nicht geschlossen sind), sondern nur 13718 [(13702 [). Das heißt, es werden Vorkommen in Zeichenfolgen und in Kontexten gezählt, die mit dieser Frage nichts zu tun haben. Dies ist also nicht wirklich zutreffend, selbst wenn wir ignorieren, dass die Codebasis möglicherweise nicht repräsentativ ist (beachten Sie, dass diese Verzerrung in beide Richtungen gehen kann). Immer noch ein Faktor von 2,8?
1

C (und später C ++ und C #) erbte seinen Stil von seinem Vorgänger B , der 1969 von Ken Thompson (mit Beiträgen von Dennis Ritchie) geschrieben wurde.

Dieses Beispiel stammt aus der Benutzerreferenz zu B von Ken Thompson (über Wikipedia ):

/* The following function will print a non-negative number, n, to
   the base b, where 2<=b<=10,  This routine uses the fact that
   in the ASCII character set, the digits 0 to 9 have sequential
   code values.  */

printn(n,b) {
        extern putchar;
        auto a;

        if(a=n/b) /* assignment, not test for equality */
                printn(a, b); /* recursive */
        putchar(n%b + '0');
}

B selbst basierte wieder auf BCPL , einer Sprache, die Martin Richards 1966 für das Multics-Betriebssystem geschrieben hatte. In Bs Klammersystem wurden nur runde Klammern verwendet, die durch zusätzliche Zeichen modifiziert wurden (Print Fakultäten, Beispiel von Martin Richards, über Wikipedia ):

GET "LIBHDR"

LET START() = VALOF $(
        FOR I = 1 TO 5 DO
                WRITEF("%N! = %I4*N", I, FACT(I))
        RESULTIS 0
)$

AND FACT(N) = N = 0 -> 1, N * FACT(N - 1)

Die in B und den folgenden Sprachen "{...}" verwendeten geschweiften Klammern sind eine Verbesserung, die Ken Thompson gegenüber dem ursprünglichen zusammengesetzten Klammerstil in BCPL "$ (...) $" vorgenommen hat.

ProphetV
quelle
1
Nein. Bob Bemer ( en.wikipedia.org/wiki/Bob_Bemer ) scheint dafür verantwortlich zu sein. "... Sie haben vorgeschlagen, dass sie anstelle der Algol-Schlüsselwörter" begin "und" end "verwendet werden könnten, was genau das ist wie sie später in der C-Sprache verwendet wurden. " (von bobbemer.com/BRACES.HTM )
SChepurin
1
Das $( ... $)Format entspricht { ... }im Lexer in BCPL, wie ??< ... ??>gleichwertig ist { ... }in C. Die Verbesserung zwischen den beiden Arten in der Tastatur - Hardware ist - nicht die Sprache.