Pourquoi les langages de programmation, en particulier C, utilisent-ils des accolades et non des carrés?

96

La définition du "langage de style C" peut être pratiquement simplifiée jusqu'à "utilise des accolades ( {})." Pourquoi utilisons-nous ce caractère particulier (et pourquoi pas quelque chose de plus raisonnable, comme [], qui n'exige pas la touche Maj au moins sur les claviers américains)?

Existe-t-il un avantage réel pour la productivité du programmeur provenant de ces accolades, ou les nouveaux concepteurs de langage devraient-ils rechercher des alternatives (par exemple, les développeurs de Python)?

Wikipedia nous dit que C utilise les accolades dites, mais pas pourquoi. Une déclaration dans l'article de Wikipedia sur la liste des langages de programmation basés sur C suggère que cet élément de syntaxe est quelque peu spécial:

De manière générale, les langues de la famille C sont celles qui utilisent la syntaxe de bloc de type C (y compris les accolades pour commencer et terminer le bloc) ...

Des chatons
la source
35
La seule personne qui peut répondre à cette question est Dennis Ritchie et il est mort. Une hypothèse raisonnable est que [] ont déjà été pris pour des tableaux.
Dirk Holsopple le
2
@ DirkHolsopple Donc, il n'a laissé aucun raisonnement derrière? Drat. Aussi: deux votes négatifs sur quelque chose qui m'intéresse vraiment? Merci les gars ....
SomeKittens
1
Veuillez poursuivre la discussion sur cette question dans cette question Meta .
Thomas Owens
2
J'ai déverrouillé ce post. Veuillez conserver vos commentaires sur la question et la discussion sur la pertinence de la question Meta .
Thomas Owens
5
Cela tient probablement aussi au fait que les accolades sont utilisées dans la notation des ensembles en mathématiques, ce qui les rend un peu difficiles à utiliser pour l'accès aux éléments de tableau, plutôt que de déclarer des éléments "définis" comme des structures, des tableaux, etc. Même les langues modernes comme Python utilisent des accolades pour déclarer des ensembles et des dictionnaires. La question est donc de savoir pourquoi C a également utilisé des accolades pour déclarer la portée. Probablement parce que les concepteurs n'aimaient tout simplement pas les alternatives connues, comme BEGIN / END, et que surcharger la notation d'accès au tableau ([]) était jugé moins esthétique que la notation d'ensemble.
Charles Salvia

Réponses:

102

Les familles Algol (Algol 60 et Algol 68) et BCPL (d'où C tire son nom) sont deux des principales influences de C.

BCPL a été le premier langage de programmation pour les accolades, et les accolades ont survécu aux modifications syntaxiques et sont devenues un moyen courant de désigner les instructions de code source du programme. En pratique, sur les claviers limités du jour, les programmes sources utilisaient souvent les séquences $ (et $) à la place des symboles {et}. Les commentaires '//' à une ligne de BCPL, qui n'ont pas été repris en C, sont réapparus en C ++, et plus tard en C99.

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

BCPL a introduit et mis en œuvre plusieurs innovations qui sont devenues des éléments communs dans la conception des langues ultérieures. Il s’agissait donc du premier langage de programmation avec accolades (un utilisant {} comme délimiteurs de blocs) et du premier langage à utiliser // pour marquer les commentaires en ligne.

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

Au sein de BCPL, on voit souvent des accolades, mais pas toujours. C'était une limitation des claviers à l'époque. Les caractères $(et $)étaient lexicographiquement équivalents à {et }. Les graphes numériques et trigraphiques ont été conservés en C (bien qu’un ensemble différent pour le remplacement des accolades - ??<et ??>).

L'utilisation d'accolades a été encore affinée dans B (qui a précédé C).

De la référence des utilisateurs à B par 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');
}

Il semblerait que des accolades aient été utilisées comme main courte pour Algol beginet à l' endintérieur de celle-ci.

Je me souviens que vous les avez également inclus dans le code de la carte à 256 caractères que vous avez publié dans le MCCA, parce que je trouvais intéressant que vous ayez proposé de les utiliser à la place des mots-clés Algol «begin» et «end», ce qui est exactement le cas. comment ils ont été utilisés plus tard dans le langage C.

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


L'utilisation de crochets (en tant que remplacement suggéré dans la question) remonte encore plus loin. Comme mentionné, la famille Algol a influencé C. Dans Algol 60 et 68 (C a été écrit en 1972 et BCPL en 1966), le crochet a été utilisé pour désigner un indice dans un tableau ou une matrice.

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

Comme les programmeurs connaissaient déjà les crochets pour les tableaux dans Algol et BCPL et les accolades pour les blocs dans BCPL, il n’était ni nécessaire ni désir de changer cela lors de la création d’une autre langue.


La question mise à jour inclut un addenda de productivité pour l'utilisation d'accolades et mentionne python. Il existe d’autres ressources sur cette étude, mais la réponse se résume à «C’est une anecdote, et à quoi vous êtes habitué est ce avec quoi vous êtes le plus productif». En raison de la diversité des compétences en programmation et de la connaissance de différentes langues, il devient difficile de rendre compte de celles-ci.

Voir aussi: Dépassement de pile Existe-t-il des études statistiques indiquant que Python est «plus productif»?

Une grande partie des gains dépendrait de l'EDI (ou de l'absence de) utilisé. Dans les éditeurs basés sur vi, placer le curseur sur une ouverture / fermeture correspondante et appuyer sur %pour déplacer le curseur sur l'autre caractère correspondant. C’est très efficace avec les langages basés sur C à l’époque - moins que maintenant.

Une meilleure comparaison serait entre {}et begin/ endquelles étaient les options du jour (l’espace horizontal était précieux). De nombreuses langues Wirth étaient basées sur un beginet le endstyle (Algol (mentionné ci - dessus), pascals (beaucoup sont familiers avec), et la famille Modula).

J'ai du mal à trouver un élément qui isole cette fonction linguistique spécifique - au mieux, je peux montrer que les langues à accolades bouclées sont beaucoup plus populaires que les langues de début à la fin et qu'il s'agit d'une construction commune. Comme mentionné dans le lien Bob Bemer ci-dessus, l'accolade a été utilisée pour faciliter la programmation en sténographie.

De Pourquoi Pascal est pas mon préféré langage de programmation

Les programmeurs de C et Ratfor trouvent «begin» et «end» volumineux par rapport à {et}.

C'est à peu près tout ce qui peut être dit - sa familiarité et ses préférences.

Communauté
la source
14
Maintenant, tout le monde ici apprend BCPL au lieu de travailler :)
Denys Séguret le
Les trigraphes (introduits dans la norme ISO C de 1989) pour {et }sont ??<et ??>. Les digraphes (introduits par l’amendement de 1995) sont <%et %>. Les trigraphes sont développés dans tous les contextes, à un stade très précoce de la traduction. Les graphes numériques sont des jetons et ne sont pas développés en littéraux de chaîne, constantes de caractère ou commentaires.
Keith Thompson
Il y avait quelque chose d’avant 1989 dans C (il faudrait que je détache mon premier livre pour avoir une date). Les pages de code EBCDIC ne comportaient pas toutes une accolade (ou des crochets), et cela était prévu dans les premiers compilateurs C.
@NevilleDNZ BCPL a utilisé des accolades en 1966. L’origine d’Algol68 serait intéressante à explorer - mais BCPL ne l’a pas obtenue d’Algo68. L’opérateur ternaire est quelque chose qui m’intéresse et que j’ai retracé jusqu’à CPL (1963) (le prédécesseur de BCPL), qui l’a empruntée à Lisp (1958).
1968: Algol68 autorise les parenthèses rondes (~) en abrégé des blocs de symboles gras début / fin . Celles-ci sont appelées symboles brefs , cf wp: Algol68 Symboles gras , cela permet aux blocs de code d’être traités comme des expressions . A68 a aussi brève sténographies comme de C : opérateur ternaire par exemple au lieu de C de . De même, cela s'applique aux déclarations if & case . ¢ BTW: A68 est l'endroit d'où provient le shell esac & fi ¢x:=(c|s1|s2)x=c?s1|s2
NevilleDNZ
24

Les accolades carrées []sont plus faciles à taper depuis le terminal IBM 2741 "largement utilisé sur le système d’ exploitation Multics" , qui avait à son tour Dennis Ritchie, l’un des créateurs du langage C, en tant que membre de l’équipe de développement .

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

Notez l' absence d'accolades chez IBM 2741!

En C, les accolades carrées sont "prises" car elles sont utilisées pour les tableaux et les pointeurs . Si les concepteurs de langage s'attendaient à ce que les tableaux et les pointeurs soient plus importants / utilisés plus fréquemment que les blocs de code (ce qui semble être une hypothèse raisonnable à leurs côtés, plutôt que sur le contexte historique du style de codage ci-dessous), cela signifierait que les accolades seraient moins importantes. "syntaxe.

L’importance des tableaux est assez évidente dans l’article Le développement du langage C de Ritchie. Il existe même une hypothèse explicite de "prévalence de pointeurs dans les programmes C" .

... le nouveau langage a conservé une explication cohérente et exploitable (s’il est inhabituel) de la sémantique des tableaux ... Deux idées sont les plus caractéristiques du C parmi les langages de sa classe: la relation entre les tableaux et les pointeurs ... L’autre trait caractéristique de C, son traitement des tableaux ... a de véritables vertus . Bien que la relation entre les pointeurs et les tableaux soit inhabituelle, vous pouvez l'apprendre. De plus, le langage montre un pouvoir considérable pour décrire des concepts importants, par exemple des vecteurs dont la longueur varie au moment de l'exécution, avec seulement quelques règles et conventions de base ...


Pour mieux comprendre le contexte historique et le style de codage de l'époque de la création du langage C, il convient de prendre en compte le fait que "l'origine de C est étroitement liée au développement de l'Unix" et, en particulier, que le transfert du système d'exploitation vers un PDP- 11 "ont conduit au développement d’une première version de C" ( source de citations ). Selon Wikipedia , "en 1972, Unix a été réécrit dans le langage de programmation C" .

Le code source de diverses anciennes versions d'Unix est disponible en ligne, par exemple sur le site de l' arbre Unix . Parmi les différentes versions présentées, la plus pertinente semble être la deuxième édition Unix de 1972-06:

La deuxième édition d'Unix a été développée pour le PDP-11 chez Bell Labs par Ken Thompson, Dennis Ritchie et d'autres. Il a étendu la première édition avec plus d'appels système et plus de commandes. Cette édition a également vu le début du langage C, qui était utilisé pour écrire certaines des commandes ...

Vous pouvez parcourir et étudier le code source en C depuis la page Second Edition Unix (V2) pour avoir une idée du style de codage typique de l'époque.

Un exemple frappant qui soutient l'idée qu'il était alors très important pour le programmeur de pouvoir taper facilement des crochets se trouve dans le code source 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;

Il est intéressant de noter comment la motivation pragmatique de la sélection de caractères pour désigner des éléments de syntaxe de langage basés sur leur utilisation dans des applications pratiques ciblées ressemble à la loi de Zipf, comme expliqué dans cette réponse géniale ...

la relation observée entre la fréquence et la longueur est appelée loi de Zipf

... à la seule différence que la longueur indiquée ci-dessus est remplacée par / généralisée par la vitesse de frappe.

moucheron
la source
5
Quelque chose à l’appui de cette attente «apparente» des concepteurs de langage? Il ne faut pas beaucoup de programmation en C pour remarquer que les accolades sont beaucoup plus courantes que les déclarations de tableau. Cela n'a pas beaucoup changé depuis les temps anciens - jetez un coup d'œil à K & R.
1
Je doute en quelque sorte de cette explication. Nous ne savons pas ce à quoi nous nous attendions et ils auraient facilement pu choisir l'inverse, car ce sont eux aussi qui ont décidé de la notation par tableaux. Nous ne savons même pas s'ils pensaient que les accolades étaient l'option "moins importante", peut-être qu'ils préféraient les accolades.
Thorsten Müller
3
@gnat: Les accolades carrées sont plus faciles à taper sur les claviers modernes. Cela s'applique-t-il aux claviers qui existaient déjà lorsque Unix et c ont été mis en œuvre pour la première fois? Je n'ai aucune raison de penser qu'ils utilisaient le même clavier, ou qu'ils supposeraient que d'autres claviers seraient comme leurs claviers, ou qu'ils auraient pensé que la vitesse de frappe aurait intérêt à être optimisée à un caractère.
Michael Shaw
1
De plus, la loi de Zipf est une généralisation de ce qui finit par se passer dans les langues naturelles. C a été construit artificiellement, il n’ya donc aucune raison de penser que cela s’appliquerait ici à moins que les concepteurs de C décident consciemment de l’appliquer délibérément. Si cela s’applique, il n’ya aucune raison de penser que cela simplifierait un élément déjà aussi court qu’un seul caractère.
Michael Shaw
1
@gnat FWIW, grep -Fome dit les *.cfichiers du code source de CPython (rév. 4b42d7f288c5 car c’est ce que j’ai sous la main), qui inclut libffi, contient 39511 {(39508 {, pourquoi deux accolades ne sont pas fermées), mais seulement 13718 [(13702) [). Compter les occurrences dans les chaînes et dans des contextes sans rapport avec cette question, donc ce n’est pas vraiment exact, même si nous ignorons que la base de code peut ne pas être représentative (notez que ce biais peut aller dans un sens ou dans l’autre). Encore un facteur de 2,8?
1

C (et par la suite C ++ et C #) ont hérité son style de renforcement de son prédécesseur B , écrit par Ken Thompson (avec la contribution de Dennis Ritchie) en 1969.

Cet exemple provient de la référence des utilisateurs à B de Ken Thompson (via 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 lui-même était à nouveau basé sur BCPL , un langage écrit par Martin Richards en 1966 pour le système d’exploitation Multics. Le système de contreventement de B n'utilisait que des accolades rondes, modifiées par des caractères supplémentaires (exemple de factorielles d'impression de Martin Richards, via 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)

Les accolades utilisées dans B et les langues suivantes "{...}" constituent une amélioration apportée par Ken Thompson au style d'accolade composé précédent dans BCPL "$ (...) $".

ProphetV
la source
1
No. Apparemment, Bob Bemer ( en.wikipedia.org/wiki/Bob_Bemer ) est responsable de cela - "... vous avez proposé qu'ils puissent être utilisés à la place des mots clés Algol" begin "et" end ", ce qui est exactement comment ils ont été utilisés plus tard dans la langue C. " (de bobbemer.com/BRACES.HTM )
SChepurin le
1
Le $( ... $)format est équivalent à celui { ... }du lexer dans BCPL, tout comme ??< ... ??>à celui { ... }de C. L'amélioration entre les deux styles réside dans le matériel du clavier et non dans la langue.