Чому мови програмування, особливо С, використовують фігурні дужки, а не квадратні?


96

Визначення "мови C-Style" практично можна спростити до "використовує фігурні дужки ( {})". Чому ми використовуємо саме цей символ (а чому б не щось більш розумне, наприклад [], яке не вимагає клавіші переключення принаймні на американських клавіатурах)?

Чи є якась користь для продуктивності програміста, що виходить із цих брекетів, чи повинні нові дизайнери мови шукати альтернативи (тобто хлопці, які стоять за Python)?

У Вікіпедії йдеться про те, що C використовує вказані дужки, але не чому. Заява у статті Вікіпедії у Переліку мов програмування на основі С говорить про те, що цей синтаксичний елемент є дещо особливим:

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


35
Єдина людина, яка може відповісти на це - Денніс Річі і він мертвий. Слушною здогадкою є те, що [] вже були взяті для масивів.
Дірк Холсоппл

2
@DirkHolsopple Так він не залишив ніяких міркувань позаду? Драт. Також: два голоси щодо чогось, про що мені справді цікаво? Дякую, хлопці ....
SomeKittens

1
Продовжуйте обговорення цього питання в цьому мета-запитанні .
Томас Оуенс

2
Я розблокував цю посаду. Будь ласка, зберігайте будь-які коментарі щодо питання та обговорення відповідності мета-питання .
Томас Оуенс

5
Це, мабуть, також має щось спільне з тим, що фігурні дужки використовуються в наборі позначень у математиці, що робить їх дещо незручним для використання для доступу до елементів масиву, а не таких речей, як оголошення "встановити" - таких речей, як структури, масиви тощо. Навіть сучасні мови на зразок Python використовують фігурні дужки, щоб оголосити набори та словники. Тоді питання: чому C також використовував фігурні дужки, щоб оголосити область застосування? Можливо, тому, що дизайнерам просто не сподобалися відомі альтернативи, такі як BEGIN / END, і перевантаження нотацій доступу до масиву ([]) вважалося менш естетично здоровим, ніж задані позначення.
Чарльз Сальвія

Відповіді:


102

Двома найважливішими впливами на C були сімейство мов Алгол (Algol 60 та Algol 68) та BCPL (від якої С отримала свою назву).

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

Від http://www.princeton.edu/~achaney/tmve/wiki100k/docs/BCPL.html

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

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

У межах BCPL часто бачать фігурні брекети, але не завжди. Це було обмеженням клавіатур на той час. Символи $(і $)були лексикографічно еквівалентними {і }. Диграфи та триграфи зберігалися в С (хоча інший набір для заміни фігурних брекетів - ??<і ??>).

Використання фігурних брекетів було додатково уточнено в 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 'start' та 'end', що саме як вони пізніше були використані мовою С.

Від http://www.bobbemer.com/BRACES.HTM


Використання квадратних дужок (як запропонована заміна у запитанні) іде ще більше. Як уже згадувалося, сім'я Алгол впливала на C. У межах Algol 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 є "більш продуктивним"?

Значна частина прибутків залежатиме від IDE (або відсутності), який використовується. У редакторах на основі vi, переміщення курсору над одним відповідним відкриттям / закриттям і натисканням %потім перемістить курсор на інший відповідний символ. Це дуже ефективно для мов на основі С ще в давні часи - тим більше зараз.

Краще порівняння було б між {}та begin/ endщо було варіантами дня (горизонтальний простір був дорогоцінним). Багато Wirth мови були засновані на beginі endстилі (Алголь (згаданий вище), Паскаль (багато з них знайомі з), і сім'єю Modula).

Мені важко знайти будь-яку, яка виділяє цю специфічну мовну особливість - у кращому випадку я можу це показати, що фігурні мови підтяжки набагато популярніші, ніж початкові кінцеві мови, і це звичайна конструкція. Як згадувалося у посиланні Боба Бемера вище, фігурна дужка була використана для полегшення програмування як скорочення.

З чого «Паскаль» не є моєю улюбленою мовою програмування

Програмісти C і Ratfor виявляють "початок" і "кінець" об'ємним порівняно з {і}.

Що стосується всього, що можна сказати - його знайомства та уподобання.


14
Зараз усі тут навчаються BCPL замість роботи :)
Denys Séguret

У тріграфи (введені в стандарті 1989 ISO C) для {і }є ??<та ??>. Диграфи (внесені поправкою 1995 р.) Є <%і %>. Триграфи розгорнуті у всіх контекстах, на дуже ранній фазі перекладу. Диграфи є лексемами, і вони не розширюються в рядкових літералах, константах символів або коментарях.
Кіт Томпсон,

До 1989 року для цього в C існувало щось (я повинен був би викопати свою першу книгу видання, щоб побачити дату). Не всі кодові сторінки EBCDIC мали фігурні дужки (або квадратні дужки), і це було передбачено в самих ранніх компіляторах C.

@NevilleDNZ BCPL використовував фігурні дужки в 1966 році. Там, де Algol68 отримав своє поняття, було б щось вивчити - але BCPL не отримав його від Algo68. Побічний оператор - це те, що мене зацікавило, і я простежив його до CPL (1963) (попередник BCPL), який запозичив це поняття у Lisp (1958).

1968: Algol68 допускає круглі дужки (~) як скорочення блоків жирних символів початку ~ кінця . Вони називаються короткими символами, cf wp: Algol68 Сміливі символи , це дозволяє обробляти блоки коду так само, як вирази . A68 також має короткі Shorthands як мову C : потрійний оператор , наприклад , замість C - х . Аналогічно це стосується тверджень if & case . ¢ BTW: A68 - це місце, де отримана оболонка, це esac & fi ¢x:=(c|s1|s2)x=c?s1|s2
NevilleDNZ

24

Квадратні дужки []легше ввести, з тих пір як IBM 2741 терміналу , який був «широко використовується в Multics» OS, яка , в свою чергу , був Денніс Рітчі, один з творців мови C , як член команди розробників .

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 "призвело до розробки ранньої версії С" ( цитує джерело ). За даними Вікіпедії , "у 1972 році Unix був переписаний мовою програмування на С" .

Вихідний код різних старих версій Unix доступний в Інтернеті, наприклад, на сайті Unix Tree . З різних версій, представлених там, найбільш релевантним видається Unix другого видання 1972-06 рр .:

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

Ви можете переглянути та вивчити вихідний код C на сторінці другого видання 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
Я якось сумніваюся в цьому поясненні. Ми не знаємо, чого очікували, і вони могли легко обрати його навпаки, оскільки вони були людьми, які також вирішували про позначення масиву. Ми навіть не знаємо, чи вважали вони фігурні брекети "менш важливим" варіантом, можливо, їм більше подобалися фігурні брекети.
thorsten müller

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 році.

Цей приклад наведено з посилання користувачів на Б Кена Томпсона (через Вікіпедію ):

/* 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 "$ (...) $".


1
Ні. Здається, Боб Бемер ( en.wikipedia.org/wiki/Bob_Bemer ) несе відповідальність за це - "... ви запропонували їх використовувати замість ключових слів" початок "і" кінець "Алгола, саме так як вони пізніше були використані мовою С ". (від bobbemer.com/BRACES.HTM )
SChepurin

1
$( ... $)Формат еквівалентний { ... }в Лексера в BCPL, так само , як ??< ... ??>це еквівалентно { ... }в С. Поліпшення між цими двома стилями знаходиться в апаратної клавіатури - не мова.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.