Por que as linguagens de programação, especialmente C, usam chaves e não as quadradas?

93

A definição de "linguagem C-Style" pode ser simplificada até "usa chaves ( {} )". Por que usamos esse caractere específico (e por que não algo mais razoável, como [] , que não exige a tecla shift pelo menos nos teclados dos EUA)?

Existe algum benefício real para a produtividade do programador que vem dessas chaves, ou os novos designers de linguagem devem procurar alternativas (ou seja, os caras por trás do Python)?

Wikipedia diz-nos que C usa as chaves, mas não porque . Uma declaração no artigo da Wikipedia sobre a Lista de linguagens de programação baseadas em C sugere que esse elemento de sintaxe é um tanto especial:

Broadly speaking, C-family languages are those that use C-like block syntax (including curly braces to begin and end the block)...

    
por SomeKittens 26.02.2013 / 15:59
fonte

3 respostas

102

Duas das principais influências para C foram a família de línguas Algol (Algol 60 e Algol 68) e BCPL (da qual C leva o nome).

BCPL was the first curly bracket programming language, and the curly brackets survived the syntactical changes and have become a common means of denoting program source code statements. In practice, on limited keyboards of the day, source programs often used the sequences $( and $) in place of the symbols { and }. The single-line '//' comments of BCPL, which were not taken up in C, reappeared in C++, and later in C99.

De link

BCPL introduced and implemented several innovations which became quite common elements in the design of later languages. Thus, it was the first curly bracket programming language (one using { } as block delimiters), and it was the first language to use // to mark inline comments.

De link

Dentro do BCPL, muitas vezes vemos chaves, mas nem sempre. Esta foi uma limitação dos teclados no momento. Os caracteres $( e $) eram lexicograficamente equivalentes a { e } . Diagramas e trigramas foram mantidos em C (embora um conjunto diferente para substituição de chaves - ??< e ??> ).

O uso de chaves foi refinado em B (que precedeu C).

De Referência dos Usuários para B por 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');
}

Há indicações de que chaves foram usadas como mão curta para begin e end dentro de Algol.

I remember that you also included them in the 256-character card code that you published in CACM, because I found it interesting that you proposed that they could be used in place of the Algol 'begin' and 'end' keywords, which is exactly how they were later used in the C language.

De link

O uso de colchetes (como sugestão de substituição na pergunta) remonta ainda mais longe. Como mencionado, a família Algol influenciou C. Dentro Algol 60 e 68 (C foi escrito em 1972 e BCPL em 1966), o colchete foi usado para designar um índice em um array ou matriz.

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

Como os programadores já estavam familiarizados com colchetes para matrizes em Algol e BCPL e chaves para blocos em BCPL, havia pouca necessidade ou desejo de mudar isso ao criar outro idioma.

A pergunta atualizada inclui um adendo de produtividade para uso de chaves e menciona python. Existem alguns outros recursos que fazem este estudo, embora a resposta se resume a "Seu anedótico, e com o que você está acostumado é com o que é mais produtivo". Por causa das habilidades amplamente variadas em programação e familiaridade com diferentes idiomas, estas são difíceis de explicar.

Veja também: Estouro de pilha Existem estudos estatísticos que indicam que o Python é “mais produtivo”?

Grande parte dos ganhos seria dependente do IDE (ou falta de) que é usado. Nos editores baseados em vi, colocar o cursor sobre um abrir / fechar correspondente e pressionar % moverá o cursor para o outro caractere correspondente. Isso é muito eficiente com linguagens baseadas em C nos velhos tempos - menos agora.

Uma comparação melhor seria entre {} e begin / end , que eram as opções do dia (o espaço horizontal era precioso). Muitas linguagens de Wirth foram baseadas em um estilo begin e end (Algol (mencionado acima), pascal (muitos estão familiarizados com) e a família Modula).

Eu tenho dificuldade em encontrar algum que isole esse recurso de linguagem específico - na melhor das hipóteses, posso mostrar que as linguagens de chaves são muito mais populares do que as linguagens finais e é uma construção comum. Como mencionado no link de Bob Bemer acima, o chaveiro foi usado para facilitar a programação como um atalho.

De Por que Pascal não é minha linguagem de programação favorita

C and Ratfor programmers find 'begin' and 'end' bulky compared to { and }.

Que é sobre tudo o que pode ser dito - sua familiaridade e preferência.

    
por 26.02.2013 / 16:29
fonte
24

As chaves quadradas [] são mais fáceis de digitar, desde que que era "amplamente utilizado em Multics" OS, que por sua vez teve Dennis Ritchie , um dos criadores da linguagem C como membro da equipe de desenvolvimento .

Observe a ausência de chaves no layout IBM 2741!

Em C, chaves quadradas são "tomadas", pois são usadas para arrays e ponteiros . Se os projetistas de linguagem esperassem que arrays e ponteiros fossem mais importantes / usados com mais freqüência do que os blocos de código (o que soa como um suposição razoável ao seu lado, mais no contexto histórico do estilo de codificação abaixo), isso significaria que as chaves iriam à sintaxe" menos importante ".

A importância dos arrays é bastante aparente no artigo O desenvolvimento da linguagem C por Ritchie. Há até mesmo uma suposição explicitamente declarada de "prevalência de ponteiros em programas em C" .

...new language retained a coherent and workable (if unusual) explanation of the semantics of arrays... Two ideas are most characteristic of C among languages of its class: the relationship between arrays and pointers... The other characteristic feature of C, its treatment of arrays... has real virtues. Although the relationship between pointers and arrays is unusual, it can be learned. Moreover, the language shows considerable power to describe important concepts, for example, vectors whose length varies at run time, with only a few basic rules and conventions...

Para entender melhor o contexto histórico e o estilo de codificação do momento em que a linguagem C foi criada, é necessário levar em conta que "origem de C está intimamente ligada ao desenvolvimento do Unix" e , especificamente, que portar o SO para um PDP-11 "levou ao desenvolvimento de uma versão inicial de C" ( cita a origem ). De acordo com Wikipedia , "em 1972, o Unix foi reescrito na linguagem de programação C" .

O código-fonte de várias versões antigas do Unix está disponível on-line, por exemplo, no site The Unix Tree . De várias versões apresentadas, a mais relevante parece ser Segunda Edição do Unix de 1972-06 :

The second edition of Unix was developed for the PDP-11 at Bell Labs by Ken Thompson, Dennis Ritchie and others. It extended the First Edition with more system calls and more commands. This edition also saw the beginning of the C language, which was used to write some of the commands...

Você pode navegar e estudar código-fonte C a partir da página Unix (V2) do Second Edition Unix > para ter uma ideia do estilo típico de codificação da época.

Um exemplo proeminente que apóia a ideia de que, naquela época, era muito importante que o programador pudesse digitar colchetes com facilidade pode ser encontrado em código fonte 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++ == '
/* 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++ == '%pre%') return (0);
ll:;
    }
    return(1);
}

tsp;
tmp0;
tmp1;
tmp2;
tmp3;
') return (0); ll:; } return(1); } tsp; tmp0; tmp1; tmp2; tmp3;

É interessante notar como a motivação pragmática de escolher caracteres para denotar elementos de sintaxe da linguagem com base em seu uso em aplicativos práticos direcionados se assemelha à Lei de Zipf como explicado nesta incrível resposta ...

observed relationship between frequency and length is called Zipf's Law

... com a única diferença que comprimento na declaração acima é substituída por / generalizada como velocidade de digitação.

    
por 26.02.2013 / 16:05
fonte
1

C (e subsequentemente C ++ e C #) herdaram seu estilo de contração de seu predecessor B , que foi escrito por Ken Thompson (com contribuições de Dennis Ritchie) em 1969.

Este exemplo é da Referência dos Usuários para B por 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');
}

O próprio B foi novamente baseado no BCPL , uma linguagem escrita por Martin Richards em 1966 para o sistema operacional Multics. O sistema de contraventamento de B usou apenas chaves redondas, modificadas por caracteres adicionais (Exemplo de fatoriais de impressão 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)

As chaves usadas em B e os idiomas subseqüentes "{...}" são uma melhoria que Ken Thompson fez sobre o estilo de suporte composto original em BCPL "$ (...) $".

    
por 26.02.2013 / 16:34
fonte