Mengapa bahasa pemrograman, terutama C, menggunakan kurung kurawal dan bukan yang berbentuk bujur sangkar?

96

Definisi "bahasa C-Style" secara praktis dapat disederhanakan menjadi "menggunakan kurung kurawal ( {})." Mengapa kita menggunakan karakter tertentu (dan mengapa bukan sesuatu yang lebih masuk akal, seperti [], yang tidak memerlukan tombol shift setidaknya di keyboard AS)?

Apakah ada manfaat aktual untuk produktivitas programmer yang berasal dari kawat gigi ini, atau haruskah perancang bahasa baru mencari alternatif (yaitu orang-orang di belakang Python)?

Wikipedia memberi tahu kita bahwa C menggunakan kawat gigi kata, tetapi tidak mengapa. Pernyataan dalam artikel Wikipedia tentang Daftar bahasa pemrograman berbasis C menunjukkan bahwa elemen sintaksis ini agak istimewa:

Secara umum, bahasa C-family adalah yang menggunakan sintaksis blok mirip C (termasuk kurung kurawal untuk memulai dan mengakhiri blok) ...

Anak kucing
sumber
35
Satu-satunya orang yang bisa menjawab ini adalah Dennis Ritchie dan dia sudah mati. Tebakan yang masuk akal adalah bahwa [] sudah diambil untuk array.
Dirk Holsopple
2
@DirkHolsopple Jadi dia tidak meninggalkan alasan? Drat. Juga: dua downvotes pada sesuatu yang saya benar-benar ingin tahu? Terima kasih
kawan
1
Silakan lanjutkan diskusi tentang pertanyaan ini di pertanyaan Meta ini .
Thomas Owens
2
Saya telah membuka kunci posting ini. Harap simpan komentar tentang pertanyaan dan diskusi tentang kesesuaian pada pertanyaan Meta .
Thomas Owens
5
Mungkin juga ada hubungannya dengan fakta bahwa kurung kurawal digunakan dalam notasi himpunan dalam matematika, membuatnya agak canggung untuk digunakan untuk akses elemen array, daripada hal-hal seperti mendeklarasikan "set" - hal-hal sepertiish seperti struct, array, dll. Bahkan bahasa modern seperti Python menggunakan kurung kurawal untuk mendeklarasikan set dan kamus. Pertanyaannya kemudian, mengapa C juga menggunakan kurung kurawal untuk menyatakan ruang lingkup? Mungkin karena perancang hanya tidak menyukai alternatif yang diketahui, seperti BEGIN / END, dan notasi akses array yang berlebihan ([]) dianggap kurang estetis daripada notasi yang ditetapkan.
Charles Salvia

Jawaban:

102

Dua pengaruh utama C adalah keluarga bahasa Algol (Algol 60 dan Algol 68) dan BCPL (dari mana C mengambil namanya).

BCPL adalah bahasa pemrograman braket keriting pertama, dan kurung keriting selamat dari perubahan sintaksis dan telah menjadi sarana umum untuk menunjukkan pernyataan kode sumber program. Dalam praktiknya, pada papan ketik terbatas hari itu, program sumber sering menggunakan urutan $ (dan $) sebagai ganti simbol {dan}. Komentar single-line '//' dari BCPL, yang tidak diambil di C, muncul kembali di C ++, dan kemudian di C99.

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

BCPL memperkenalkan dan mengimplementasikan beberapa inovasi yang menjadi elemen yang cukup umum dalam desain bahasa selanjutnya. Jadi, itu adalah bahasa pemrograman braket keriting pertama (yang menggunakan {} sebagai pembatas blok), dan itu adalah bahasa pertama yang menggunakan // untuk menandai komentar inline.

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

Dalam BCPL, orang sering melihat kawat gigi keriting, tetapi tidak selalu. Ini adalah keterbatasan keyboard pada saat itu. Karakter $(dan $)secara leksikografis setara dengan {dan }. Digraph dan trigraph dipertahankan dalam C (meskipun set berbeda untuk penggantian kurung kurawal - ??<dan ??>).

Penggunaan kawat gigi keriting selanjutnya disempurnakan dalam B (yang mendahului C).

Dari Referensi Pengguna ke B oleh 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');
}

Ada indikasi bahwa kurung kurawal digunakan sebagai tangan pendek untuk begindan di enddalam Algol.

Saya ingat bahwa Anda juga memasukkannya dalam kode kartu 256-karakter yang Anda terbitkan di CACM, karena saya merasa menarik bahwa Anda mengusulkan agar mereka dapat digunakan sebagai pengganti kata kunci 'mulai' dan 'akhir' Algol, yang persis sama bagaimana mereka kemudian digunakan dalam bahasa C.

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


Penggunaan tanda kurung siku (sebagai pengganti yang disarankan dalam pertanyaan) kembali lebih jauh. Seperti disebutkan, keluarga Algol mempengaruhi C. Dalam Algol 60 dan 68 (C ditulis pada tahun 1972 dan BCPL pada tahun 1966), braket persegi digunakan untuk menunjuk indeks ke dalam array atau matriks.

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

Karena programmer sudah terbiasa dengan tanda kurung untuk array di Algol dan BCPL, dan kurung kurawal untuk blok di BCPL, ada sedikit kebutuhan atau keinginan untuk mengubah ini ketika membuat bahasa lain.


Pertanyaan yang diperbarui mencakup tambahan produktivitas untuk penggunaan penjepit keriting dan menyebutkan python. Ada beberapa sumber lain yang melakukan penelitian ini meskipun jawabannya bermuara pada "Ini anekdotal, dan apa yang Anda terbiasa adalah dengan apa Anda paling produktif dengan." Karena keterampilan yang sangat beragam dalam pemrograman dan keakraban dengan bahasa yang berbeda, ini menjadi sulit untuk diperhitungkan.

Lihat juga: Stack Overflow Apakah ada studi statistik yang menunjukkan bahwa Python "lebih produktif"?

Sebagian besar keuntungan akan tergantung pada IDE (atau kurangnya) yang digunakan. Pada editor berbasis vi, menempatkan kursor di atas salah satu pencocokan buka / tutup dan menekan %kemudian akan memindahkan kursor ke karakter pencocokan lainnya. Ini sangat efisien dengan bahasa berbasis C kembali di masa lalu - kurang begitu sekarang.

Perbandingan yang lebih baik adalah antara {}dan begin/ endyang merupakan pilihan hari itu (ruang horizontal sangat berharga). Banyak bahasa Wirth didasarkan pada a begindan endgaya (Algol (disebutkan di atas), pascal (banyak yang akrab dengan), dan keluarga Modula).

Saya mengalami kesulitan menemukan yang mengisolasi fitur bahasa khusus ini - yang terbaik yang bisa saya lakukan adalah menunjukkan bahwa bahasa kurung kurawal jauh lebih populer daripada bahasa akhir dan itu adalah konstruksi umum. Seperti disebutkan dalam tautan Bob Bemer di atas, kurung kurawal digunakan untuk membuatnya lebih mudah diprogram sebagai steno.

Dari Mengapa Pascal Bukan Bahasa Pemrograman Favorit Saya

Pemrogram C dan Ratfor menemukan 'awal' dan 'akhir' besar dibandingkan dengan {dan}.

Itulah semua yang bisa dikatakan - keakraban dan preferensi.

Komunitas
sumber
14
Sekarang semua orang di sini belajar BCPL daripada bekerja :)
Denys Séguret
Trigraph (diperkenalkan dalam standar ISO C 1989) untuk {dan }sedang ??<dan ??>. Digraph (diperkenalkan oleh amandemen 1995) adalah <%dan %>. Trigraph diperluas dalam semua konteks, dalam fase terjemahan yang sangat awal. Digraph adalah token, dan tidak diperluas dalam string literal, konstanta karakter, atau komentar.
Keith Thompson
Ada sesuatu sebelum 1989 untuk ini di C (saya harus menggali buku edisi pertama saya untuk mendapatkan tanggal itu). Tidak semua halaman kode EBCDIC memiliki kurung kurawal (atau kurung siku) di dalamnya, dan ada ketentuan untuk ini dalam kompiler C paling awal.
@NevilleDNZ BCPL menggunakan kurung kurawal pada tahun 1966. Di mana Algol68 memperoleh gagasannya akan menjadi sesuatu yang bisa dijelajahi - tetapi BCPL tidak mendapatkannya dari Algo68. Operator ternary adalah sesuatu yang saya tertarik dan telah melacaknya kembali ke CPL (1963) (pendahulu BCPL) yang meminjam gagasan dari Lisp (1958).
1968: Algol68 memungkinkan putaran kurung (~) sebagai singkatan dari mulai ~ akhir tebal blok simbol. Ini disebut simbol singkat , cf wp: Simbol Algol68 Bold , ini memungkinkan blok kode diperlakukan sama seperti ekspresi . A68 juga memiliki singkat shorthands seperti C :? Operator ternary misalnya x:=(c|s1|s2)bukan C x=c?s1|s2. Demikian pula ini berlaku untuk pernyataan if & case . ¢ BTW: A68 berasal dari tempat shell mendapatkan esac & fi ¢
NevilleDNZ
24

Square braces []lebih mudah diketik , sejak terminal IBM 2741 yang "banyak digunakan pada Multics" OS, yang pada gilirannya memiliki Dennis Ritchie, salah satu pembuat bahasa C sebagai anggota tim pengembang .

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

Perhatikan tidak adanya kurung kurawal pada tata letak IBM 2741!

Dalam C, kurung siku "diambil" karena ini digunakan untuk array dan pointer . Jika perancang bahasa mengharapkan array dan pointer menjadi lebih penting / digunakan lebih sering daripada blok kode (yang kedengarannya seperti asumsi yang masuk akal di pihak mereka, lebih pada konteks historis gaya pengkodean di bawah), itu berarti kurung kurawal akan mengarah ke "kurang penting "sintaks.

Pentingnya array cukup jelas dalam artikel Pengembangan Bahasa C oleh Ritchie. Bahkan ada asumsi yang secara eksplisit menyatakan "prevalensi pointer dalam program C" .

... bahasa baru mempertahankan penjelasan yang koheren dan bisa diterapkan (jika tidak biasa) dari semantik array ... Dua ide adalah ciri paling khas C di antara bahasa kelasnya: hubungan antara array dan pointer ... Fitur karakteristik lain dari C, perawatan array ... memiliki kebajikan nyata . Meskipun hubungan antara pointer dan array tidak biasa, itu bisa dipelajari. Selain itu, bahasa menunjukkan kekuatan yang cukup besar untuk menggambarkan konsep-konsep penting, misalnya, vektor yang panjangnya bervariasi pada waktu berjalan, dengan hanya beberapa aturan dasar dan konvensi ...


Untuk pemahaman lebih lanjut tentang konteks historis dan gaya pengkodean waktu ketika bahasa C dibuat, orang perlu mempertimbangkan bahwa "asal C terkait erat dengan pengembangan Unix" dan, khususnya, bahwa porting OS ke PDP- 11 "mengarah pada pengembangan versi awal C" ( sumber kutipan ). Menurut Wikipedia , "pada tahun 1972, Unix ditulis ulang dalam bahasa pemrograman C" .

Kode sumber dari berbagai versi lama Unix tersedia online, misalnya di situs The Unix Tree . Dari berbagai versi yang disajikan di sana, yang paling relevan tampaknya adalah Second Edition Unix tanggal 1972-06:

Unix edisi kedua dikembangkan untuk PDP-11 di Bell Labs oleh Ken Thompson, Dennis Ritchie dan lainnya. Ini memperluas Edisi Pertama dengan lebih banyak panggilan sistem dan lebih banyak perintah. Edisi ini juga melihat awal dari bahasa C, yang digunakan untuk menulis beberapa perintah ...

Anda dapat menelusuri dan mempelajari kode sumber C dari halaman Second Edition Unix (V2) untuk mendapatkan gambaran tentang gaya pengkodean khas waktu itu.

Sebuah contoh menonjol yang mendukung gagasan bahwa pada saat itu agak penting bagi programmer untuk dapat mengetik kurung siku dengan mudah dapat ditemukan dalam kode sumber 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;

Sangat menarik untuk dicatat bagaimana motivasi pragmatis memilih karakter untuk menunjukkan elemen sintaksis bahasa berdasarkan penggunaannya dalam aplikasi praktis yang ditargetkan menyerupai Hukum Zipf seperti yang dijelaskan dalam jawaban hebat ini ...

hubungan yang diamati antara frekuensi dan panjang disebut Hukum Zipf

... dengan satu-satunya perbedaan yang panjang dalam pernyataan di atas diganti dengan / digeneralisasi sebagai kecepatan mengetik.

agas
sumber
5
Adakah yang mendukung harapan "nyata" dari perancang bahasa ini? Tidak perlu banyak pemrograman dalam C untuk memperhatikan bahwa kurung kurawal jauh lebih umum daripada deklarasi array. Ini tidak banyak berubah sejak masa lalu - lihatlah K&R.
1
Entah bagaimana saya meragukan penjelasan ini. Kami tidak tahu apa yang diharapkan dan mereka bisa dengan mudah memilihnya sebaliknya karena mereka adalah orang-orang yang memutuskan tentang notasi array juga. Kita bahkan tidak tahu jika mereka menganggap kawat gigi keriting sebagai opsi "kurang penting", mungkin mereka lebih suka kawat gigi keriting.
thorsten müller
3
@gnat: Kurung kotak lebih mudah diketik pada keyboard modern, apakah ini berlaku pada keyboard yang ada saat unix dan c pertama kali diimplementasikan? Saya tidak punya alasan untuk curiga bahwa mereka menggunakan keyboard yang sama, atau bahwa mereka akan menganggap bahwa keyboard lain akan seperti keyboard mereka, atau bahwa mereka akan berpikir kecepatan mengetik akan layak dioptimalkan oleh satu karakter.
Michael Shaw
1
Juga, hukum Zipf adalah generalisasi tentang apa yang akhirnya terjadi dalam bahasa alami. C dibuat secara buatan, jadi tidak ada alasan untuk berpikir itu akan berlaku di sini kecuali para perancang C secara sadar memutuskan untuk menerapkannya secara sengaja. Jika itu berlaku, tidak ada alasan untuk menganggap itu akan menyederhanakan sesuatu yang sudah sesingkat satu karakter.
Michael Shaw
1
@gnat FWIW, grep -Fomemberi tahu saya *.cfile kode sumber CPython (rev. 4b42d7f288c5 karena itulah yang saya miliki), yang termasuk libffi, berisi 39511 {( 39508 {, tak tahu mengapa dua kawat gigi tidak ditutup), tetapi hanya 13718 [( 13702) [). Itu menghitung kemunculan dalam string dan dalam konteks yang tidak terkait dengan pertanyaan ini, jadi ini tidak benar-benar akurat, bahkan jika kita mengabaikan bahwa basis kode mungkin tidak representatif (perhatikan bahwa bias ini bisa mengarah ke kedua arah). Namun, faktor 2,8?
1

C (dan selanjutnya C ++ dan C #) mewarisi gaya bracing dari pendahulunya B , yang ditulis oleh Ken Thompson (dengan kontribusi dari Dennis Ritchie) pada tahun 1969.

Contoh ini dari Referensi Pengguna ke B oleh 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 sendiri sekali lagi didasarkan pada BCPL , bahasa yang ditulis oleh Martin Richards pada tahun 1966 untuk sistem Operasi Multics. Sistem bracing B hanya menggunakan kawat gigi bundar, dimodifikasi oleh karakter tambahan (Cetak faktorial contoh oleh 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)

Kurung kurawal yang digunakan dalam bahasa B dan bahasa berikutnya "{...}" adalah peningkatan yang dibuat Ken Thompson atas gaya senyawa majemuk asli dalam BCPL "$ (...) $".

NabiV
sumber
1
Tidak. Tampaknya Bob Bemer ( en.wikipedia.org/wiki/Bob_Bemer ) bertanggung jawab untuk ini - "... Anda mengusulkan agar mereka dapat digunakan sebagai pengganti kata kunci 'mulai' dan 'akhir' Algol, yang tepatnya bagaimana mereka kemudian digunakan dalam bahasa C. " (dari bobbemer.com/BRACES.HTM )
SChepurin
1
The $( ... $)Format setara dengan { ... }di lexer di BCPL, seperti ??< ... ??>setara dengan { ... }di C. Peningkatan antara dua gaya dalam hardware keyboard yang - bukan bahasa.