Почему языки программирования, особенно C, используют фигурные скобки, а не квадратные?

96

Определение «языка C-Style» практически может быть упрощено до «использования фигурных скобок ( {})». Почему мы используем этот конкретный символ (и почему не что-то более разумное, например [], которое не требует клавиши Shift, по крайней мере, на клавиатурах США)?

Есть ли какая-то реальная выгода для производительности программиста, которая исходит из этих фигурных скобок, или новые разработчики языка должны искать альтернативы (например, парни из Python)?

Википедия говорит нам, что C использует указанные скобки, но не почему. Утверждение в статье в Википедии о Списке языков программирования на C позволяет предположить, что этот синтаксический элемент несколько особенный:

Вообще говоря, языки семейства C - это те, которые используют синтаксис C-подобного блока (включая фигурные скобки для начала и конца блока) ...

SomeKittens
источник
35
Единственный человек, который может ответить на это, это Деннис Ричи, и он мертв. Разумное предположение состоит в том, что [] уже были приняты для массивов.
Дирк Холсоппл
2
@DirkHolsopple Значит, он не оставил никаких рассуждений? Убирайся. Кроме того: два отрицательных отзыва о чем-то, что мне действительно интересно? Спасибо, ребята ....
SomeKittens
1
Пожалуйста, продолжите обсуждение этого вопроса в этом мета-вопросе .
Томас Оуэнс
2
Я разблокировал этот пост. Пожалуйста, оставляйте любые комментарии по поводу вопроса и обсуждения уместности по мета-вопросу .
Томас Оуэнс
5
Возможно, это также связано с тем фактом, что фигурные скобки используются в нотации множеств в математике, что делает их несколько неудобными для доступа к элементам массива, а не такими вещами, как объявление «множественных» вещей, таких как структуры, массивы и т. Д. Даже современные языки, такие как Python, используют фигурные скобки для объявления наборов и словарей. Тогда возникает вопрос: почему C также использует фигурные скобки для объявления области? Возможно, потому что разработчикам просто не нравились известные альтернативы, такие как BEGIN / END, и перегрузка нотации доступа к массиву ([]) считалась менее эстетичной, чем нотация набора.
Чарльз Сальвия

Ответы:

102

Двумя основными факторами, влияющими на C, были семейство языков Algol (Algol 60 и Algol 68) и BCPL (от которого C берет свое название).

BCPL был первым языком программирования в фигурных скобках, и фигурные скобки пережили синтаксические изменения и стали обычным средством обозначения операторов исходного кода программы. На практике на ограниченных клавиатурах дня исходные программы часто использовали последовательности $ (и $) вместо символов {и}. Однострочные комментарии «//» BCPL, которые не были рассмотрены в C, появились в C ++, а затем в C99.

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

BCPL представил и внедрил несколько нововведений, которые стали довольно распространенными элементами в дизайне более поздних языков. Таким образом, это был первый язык программирования с фигурными скобками (один из которых использовал {} в качестве разделителей блоков), и это был первый язык, который использовал // для пометки встроенных комментариев.

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

В BCPL часто можно увидеть фигурные скобки, но не всегда. Это было ограничение клавиатуры в то время. Символы $(и $)были лексикографически эквивалентны {и }. Органы и триграфы были сохранены в C (хотя другой набор для замены фигурных скобок - ??<и ??>).

Использование фигурных скобок было дополнительно уточнено в B (что предшествовало C).

Из ссылки пользователей на Б Кена Томпсона:

/* 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');
}

Есть признаки того, что фигурные скобки использовались в качестве короткой руки для beginи endвнутри Алгола.

Я помню, что вы также включили их в 256-символьный карточный код, который вы опубликовали в CACM, потому что мне показалось интересным, что вы предложили, чтобы они могли использоваться вместо ключевых слов algol 'begin' и 'end', что точно как они были позже использованы в языке Си.

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


Использование квадратных скобок (в качестве предлагаемой замены в вопросе) восходит еще дальше. Как уже упоминалось, семья Алгол повлияла на C. В Алголе 60 и 68 (C был написан в 1972 году и BCPL в 1966 году) квадратная скобка использовалась для обозначения индекса в массиве или матрице.

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

Поскольку программисты уже были знакомы с квадратными скобками для массивов в Algol и BCPL и фигурными скобками для блоков в BCPL, не было особой необходимости или желания изменить это при создании другого языка.


Обновленный вопрос включает в себя дополнение производительности для использования фигурных скобок и упоминает Python. Есть некоторые другие ресурсы, которые делают это исследование, хотя ответ сводится к «Это анекдотично, и то, к чему вы привыкли, это то, с чем вы наиболее продуктивны». Из-за различий в навыках программирования и знакомства с разными языками, их становится трудно объяснить.

См. Также: Переполнение стека. Существуют ли статистические исследования, показывающие, что Python «более продуктивен»?

Большая часть выигрышей будет зависеть от используемой IDE (или ее отсутствия). В редакторах на основе vi при наведении курсора на одно соответствующее открытие / закрытие и нажатии %переместите курсор на другой соответствующий символ. Это очень эффективно с языками на основе C в прежние времена - теперь меньше.

Лучшее сравнение было бы между {}и begin/ endкоторые были вариантами дня (горизонтальное пространство было драгоценным). Многие виртские языки были основаны на стиле beginи endстиле (Алгол (упомянутый выше), паскаль (многие знакомы с ним) и семья Модула).

Мне трудно найти что-то, что изолирует эту особенность языка - в лучшем случае я могу показать, что языки с фигурными скобками гораздо более популярны, чем языки начального и конечного языков, и это общая конструкция. Как упомянуто в ссылке Боба Бемера выше, фигурная скобка использовалась, чтобы упростить программирование как сокращение.

От Почему Паскаль не мой любимый язык программирования

Программисты C и Ratfor считают «начало» и «конец» громоздкими по сравнению с {и}.

Что обо всем, что можно сказать - его знакомство и предпочтения.

Сообщество
источник
14
Теперь все здесь изучают BCPL вместо того, чтобы работать :)
Денис Сегюре
Триграфы (введенные в стандарте ISO C 1989 года) для {и }являются ??<и ??>. Органы (введенные поправкой 1995 года) - это <%и %>. Триграфы расширяются во всех контекстах, на очень ранней стадии перевода. Органы являются токенами и не раскрываются в строковых литералах, символьных константах или комментариях.
Кит Томпсон
Что-то существовало до 1989 года для этого в Си (мне нужно было выкопать мою первую книгу изданий, чтобы узнать дату). Не во всех кодовых страницах EBCDIC есть фигурные скобки (или квадратные скобки), и в самых ранних компиляторах языка C это было предусмотрено.
@NevilleDNZ BCPL использовал фигурные скобки в 1966 году. Откуда Алгол68 получил свое представление, было бы что-то исследовать, но BCPL не получил его от Алго68. Тернарный оператор - это то, что меня заинтересовало, и я проследил его до CPL (1963) (предшественник BCPL), который позаимствовал это понятие у Lisp (1958).
1968: Algol68 позволяет круглые скобки (~) в качестве стенографии от начать ~ конечных дерзкие блоков символов. Они называются краткими символами, см . Wp: Algol68 Жирные символы , это позволяет обрабатывать блоки кода точно так же, как выражения . A68 также имеет краткие Shorthands как язык C : тройной оператор , например , x:=(c|s1|s2)вместо C - х x=c?s1|s2. Аналогично это относится к операторам if & case . ¢ КСТАТИ: A68 оттуда, где оболочка получила его esac & fi ¢
NevilleDNZ
24

Квадратные скобки []легче вводить, начиная с терминала IBM 2741, который "широко использовался в ОС Multics" , в который в свою очередь входил Деннис Ритчи, один из создателей языка Си, в качестве члена команды разработчиков .

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

Обратите внимание на отсутствие фигурных скобок на макете IBM 2741!

В C квадратные скобки «взяты», поскольку они используются для массивов и указателей . Если бы разработчики языка ожидали, что массивы и указатели будут более важными / использованными чаще, чем кодовые блоки (что звучит как разумное предположение с их стороны, более подробно об историческом контексте стиля кодирования ниже), это означало бы, что фигурные скобки переходят к «менее важным». "синтаксис.

Важность массивов довольно очевидна в статье Ритчи «Развитие языка Си ». Есть даже явно заявленное предположение о «распространенности указателей в программах на Си» .

... новый язык сохранил последовательное и работоспособное (если не необычное) объяснение семантики массивов ... Для языков своего класса C наиболее характерны две идеи : отношения между массивами и указателями ... Другая характерная особенность С, его обработка массивов ... имеет реальные достоинства . Хотя отношения между указателями и массивами необычны, их можно изучить. Более того, язык демонстрирует значительную мощность для описания важных понятий, например, векторов, длина которых изменяется во время выполнения, с несколькими базовыми правилами и соглашениями ...


Для дальнейшего понимания исторического контекста и стиля кодирования того времени, когда был создан язык C, необходимо принять во внимание, что «происхождение C тесно связано с разработкой Unix» и, в частности, это перенос ОС на PDP- 11 «привело к разработке ранней версии C» ( источник цитаты ). Согласно Википедии , «в 1972 году Unix был переписан на языке программирования Си» .

Исходный код различных старых версий Unix доступен онлайн, например, на сайте The Unix Tree . Из различных представленных там версий наиболее актуальным представляется второе издание Unix от 1972-06:

Второе издание Unix было разработано для PDP-11 в Bell Labs Кеном Томпсоном, Деннисом Ричи и другими. Он расширил первое издание большим количеством системных вызовов и большим количеством команд. В этом издании также появилось начало языка Си, который использовался для написания некоторых команд ...

Вы можете просмотреть и изучить исходный код на языке Си со страницы второго издания Unix (V2), чтобы получить представление о типичном стиле кодирования того времени.

Яркий пример, подтверждающий идею о том, что в то время программисту было достаточно легко набирать квадратные скобки, можно найти в исходном коде 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;

Интересно отметить, что прагматическая мотивация выбора символов для обозначения элементов синтаксиса языка, основанного на их использовании в целевых практических приложениях, напоминает закон Ципфа, как объясняется в этом потрясающем ответе ...

Наблюдаемая связь между частотой и длиной называется законом Ципфа

... с той лишь разницей, что длина в вышеприведенном выражении заменяется / обобщается как скорость набора текста.

комар
источник
5
Что-нибудь в поддержку этого "очевидного" ожидания языковыми дизайнерами? Не нужно много программировать на C, чтобы заметить, что фигурные скобки встречаются гораздо чаще, чем объявления массивов. Это не сильно изменилось с давних времен - взгляните на K & R.
1
Я почему-то сомневаюсь в этом объяснении. Мы не знаем, что ожидалось, и они могли бы легко выбрать его наоборот, так как они были людьми, которые тоже должны были принять решение о записи массива. Мы даже не знаем, считали ли они фигурные скобки «менее важным» вариантом, возможно, им больше нравились фигурные скобки.
Торстен Мюллер
3
@gnat: Квадратные скобки легче вводить на современных клавиатурах, относится ли это к клавиатурам, существовавшим в то время, когда Unix и C были впервые внедрены? У меня нет оснований подозревать, что они используют одну и ту же клавиатуру или что они предполагают, что другие клавиатуры будут такими же, как их клавиатуры, или что они думали, что скорость набора текста будет стоить оптимизировать одним символом.
Майкл Шоу
1
Кроме того, закон Ципфа является обобщением того, что происходит на естественных языках. C был создан искусственно, поэтому нет никаких оснований полагать, что он применим здесь, если разработчики C сознательно не решили сознательно применять его. Если это применимо, нет никаких оснований предполагать, что это упростит что-то уже такое короткое, как один символ.
Майкл Шоу
1
@gnat FWIW, grep -Foсообщает мне *.cфайлы исходного кода CPython (rev. 4b42d7f288c5, потому что это то, что у меня под рукой), который включает libffi, содержит 39511 {(39508 {, не знаю, почему две скобки не закрыты), но только 13718 [(13702 [). Это подсчет вхождений в строках и в контекстах, не связанных с этим вопросом, так что это не совсем точно, даже если мы игнорируем, что база кода может быть не репрезентативной (обратите внимание, что это смещение может идти в любом направлении). Тем не менее, фактор 2,8?
1

C (а затем C ++ и C #) унаследовал свой бодрящий стиль от своего предшественника B , который был написан Кеном Томпсоном (с участием Денниса Ричи) в 1969 году.

Этот пример взят из «Ссылки пользователя на B» Кена Томпсона (из Википедии ):

/* 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 снова был основан на BCPL , языке, написанном Мартином Ричардсом в 1966 году для операционной системы Multics. Система крепления B использует только круглые скобки, модифицированные дополнительными символами (пример печати факториала Мартина Ричардса через Википедию ):

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)

Фигурные скобки, используемые в B и последующих языках "{...}", - это улучшение, которое Кен Томпсон сделал по сравнению с оригинальным составным стилем скобок в BCPL "$ (...) $".

ProphetV
источник
1
Нет. Похоже, что за это отвечает Боб Бемер ( en.wikipedia.org/wiki/Bob_Bemer ) - «... вы предлагали использовать их вместо ключевых слов« начало »и« конец », то есть как они были позже использованы в языке Си ". (от bobbemer.com/BRACES.HTM )
Шепурин
1
$( ... $)Формат эквивалентен { ... }в лексере в BCPL, так же , как ??< ... ??>это эквивалентно { ... }в С. Улучшение между этими двумя стилями находится в аппаратной клавиатуры - не язык.