Neden programlama dilleri, özellikle de C, köşeli parantez kullanıyor, kare dili kullanmıyor?


96

"C-Stili dili" nin tanımı pratikte "kıvrımlı ayraçlar ( {}) " kullanılarak basitleştirilebilir . Bu özel karakteri neden kullanıyoruz (ve neden []en azından ABD klavyelerinde shift tuşunu gerektirmeyen daha makul bir şey değil)?

Bu parantezlerden gelen programcı verimliliğine gerçek bir fayda var mı, yoksa yeni dil tasarımcıları alternatifler aramalı mı (yani Python'un arkasındaki adamlar)?

Wikipedia bize C'nin kullandığı telleri kullandığını söylüyor ; C tabanlı programlama dilleri listesindeki Wikipedia makalesinde yer alan bir ifade , bu sözdizimi öğesinin biraz özel olduğunu göstermektedir:

Genel olarak konuşursak, C ailesi dilleri , C benzeri blok sözdizimini kullanan dillerdir (bloğa başlamak ve sonlandırmak için kaşlı ayraçlar dahil ) ...


35
Buna cevap verebilecek tek kişi Dennis Ritchie ve öldü. Makul bir tahmin, [] dizileri için zaten alınmış.
Dirk Holsopple

2
@DirkHolsopple Yani geride hiçbir sebep bırakmadı mı? Drat. Ayrıca: Gerçekten merak ettiğim bir şey için iki oy? Thanks guys ....
SomeKittens

1
Lütfen bu Meta sorudaki tartışmaya devam edin .
Thomas Owens

2
Bu yayının kilidini açtım. Lütfen soru hakkındaki yorumları ve Meta soruya uygunluğuyla ilgili tartışmaları saklayın .
Thomas Owens

5
Muhtemelen ayrıca küme parantezlerinin matematiğin set gösteriminde kullanılması gerçeği ile ilgisi vardır, bunları "set" ilan etmek gibi yapılar, diziler, vb. Python gibi modern diller bile kümeleri ve sözlükleri bildirmek için kaşlı ayraçlar kullanır. Öyleyse soru, C neden kapsam belirtmek için kaşlı ayraçlar kullandı? Muhtemelen, tasarımcılar BEGIN / END gibi bilinen alternatifleri beğenmediler ve aşırı yükleme dizisi erişim gösterimi ([]) ayarlanan gösterime göre daha az estetik olarak kabul edildi.
Charles Salvia

Yanıtlar:


102

C'nin en önemli etkilerinden ikisi Algol dil ailesi (Algol 60 ve Algol 68) ve BCPL (C'nin adını aldığı) olmuştur.

BCPL ilk küme parantezi programlama diliydi ve küme parantezleri sözdizimsel değişikliklerden kurtuldu ve program kaynak kodu ifadelerini ifade etmek için yaygın bir araç haline geldi. Uygulamada, günün sınırlı klavyelerinde, kaynak programları genellikle {ve} sembollerinin yerine $ (ve $) dizilerini kullanır. Tek satırlı '//' BCPL'nin C'ye alınmamış olan yorumları C ++ ve daha sonra C99'da yeniden ortaya çıktı.

Gönderen http://www.princeton.edu/~achaney/tmve/wiki100k/docs/BCPL.html

BCPL, daha sonraki dillerin tasarımında oldukça yaygın olan unsurlar haline gelen birkaç yenilik getirdi ve uyguladı. Böylece, ilk küme parantezi programlama dili (biri blok ayırıcı olarak {} kullanan) ve satır içi yorumları işaretlemek için // kullanılan ilk dildi.

Gönderen http://progopedia.com/language/bcpl/

BCPL içinde sık sık kaşlı ayraçlar görülür, ancak her zaman değil. Bu o zaman klavyelerin bir sınırlamasıydı. Karakterler $(ve $)sözlüksel olarak {ve ile eşdeğerdi }. Digraphs ve üç karakterli C içinde muhafaza edilmiştir (küme ayracı değiştirilmesi için bir dizi farklı olsa da - ??<ve ??>).

Kıvrımlı ayraçların kullanımı B'de (C'den önce) daha da arındırılmıştır.

Gönderen B'ye Kullanıcıların Referans Ken Thompson tarafından:

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

Kıvrımlı kaşlı ayraçların Algol için beginve endAlgol için kısa el olarak kullanıldığına dair göstergeler var .

Bunları CACM'de yayınladığınız 256 karakterlik kart koduna da dahil ettiğinizi hatırlıyorum, çünkü bunların Algol 'başlangıç' ve 'son' anahtar kelimelerinin yerine kullanılmasını önerdiğinizi ilginç buldum. daha sonra C dilinde nasıl kullanıldıkları.

Gönderen http://www.bobbemer.com/BRACES.HTM


Köşeli parantezlerin kullanılması (soruda önerilen bir yedek olarak) daha da geriye gider. Bahsedildiği gibi, Algol ailesi C'yi etkilemiştir. Algol 60 ve 68'de (C, 1972'de ve 1966'da BCPL yazılmıştır), köşeli parantez bir diziyi veya matrisi içine bir indeks belirlemek için kullanılmıştır.

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

Programcılar Algol ve BCPL'deki diziler için köşeli parantezlere ve BCPL'deki bloklar için küme parantezlerine aşina olduklarından, başka bir dil yaparken bunu değiştirmek için çok az ihtiyaç veya istek vardı.


Güncellenen soru, küme ayracı kullanımı için bir üretkenlik eki içerir ve python'dan bahseder. Bu çalışmayı yapan başka kaynaklar da var: “Bu fıkra, alışkın olduğun şey, en verimli olduğun şey”. Programlama konusundaki çok çeşitli beceriler ve farklı dillere aşinallık nedeniyle bunların hesaba katılması zorlaşır.

Ayrıca bakınız: Yığın Taşması Python'un “daha ​​üretken” olduğunu gösteren istatistiksel çalışmalar var mı?

Kazançların çoğu, kullanılan IDE'ye (veya eksikliğe) bağlı olacaktır. Vi tabanlı editörlerde, imleci bir eşleştirmenin üzerine açıp / kapatarak tuşuna basarak %imleci diğer eşleştirme karakterine götürür. Bu, eski günlerde C tabanlı dillerde çok verimlidir - şimdi daha az.

Daha iyi bir karşılaştırma , günün seçenekleri idi {}ve begin/ arasında olacaktı end(yatay alan değerliydi). Birçok Wirth dili bir beginve endstile dayanıyordu (Algol (yukarıda adı geçen)), pascal (çoğu tanıdık) ve Modula ailesi.

Bu özel dil özelliğini izole eden herhangi birisini bulmakta zorluk çekiyorum - en iyisini yapabilirim, kaşlı ayraç dillerinin başlangıçtaki dillerden çok daha popüler olduğunu ve ortak bir yapı olduğunu göstermek. Yukarıdaki Bob Bemer bağlantısında belirtildiği gibi, küme parantezi kısayol olarak programlamayı kolaylaştırmak için kullanılmıştır.

Gönderen Pascal Favori Programlama Dili Değildir Neden

C ve Ratfor programcıları {ve} ile karşılaştırıldığında 'başlangıç' ve 'son' hantal buluyorlar.

Bu söylenebilecek her şey hakkında - onun yakınlığı ve tercihi.


14
Şimdi buradaki herkes çalışmak yerine BCPL'yi öğreniyor :)
Denys Séguret

(1989 ISO C standardında tanıtılan) üç karakterli {ve }vardır ??<ve ??>. Digraphs (1995 değişikliği ile getirilen) <%ve %>. Trigraph'lar tüm bağlamlarda, çok erken bir çeviri aşamasında genişletilir. Digraph'lar belirteçlerdir ve dize değişmezleri, karakter sabitleri veya yorumlar halinde genişletilmezler.
Keith Thompson

1989'dan önce C'de bunun için bir şeyler vardı (bunun üzerine bir tarih almak için ilk baskı kitabımı çıkarmam gerekiyordu). Tüm EBCDIC kod sayfalarında bir küme ayracı (veya köşeli ayraç) yoktu ve bunun için en eski C derleyicilerinde bazı hükümler vardı.

@NevilleDNZ BCPL, 1966'da kaşlı ayraçlar kullandı. Algol68'in nosyondan alması neyin keşfedilmesi gereken bir şey olurdu - ama BCPL Algo68'den alamadı. Üçlü operatör ilgi duyduğum bir şeydi ve onu Lisp'ten (1958) alan ödünç alan CPL'ye (1963) (BCPL'nin öncüsü) kadar izini sürdü.

1968: Algol68 izin yuvarlak bir kısaltma olarak parantez (~) başlayacak ~ cesur sembol blokları. Bunlara kısa semboller denir , cf wp: Algol68 Kalın semboller , bu kod bloklarına tıpkı ifadeler gibi davranılmasını sağlar . A68 ayrıca sahip kısa C'nin gibi kestirme ? Üçlü operatör: ör x:=(c|s1|s2)yerine C'ler x=c?s1|s2. Benzer şekilde if & case deyimleri için de geçerlidir . ¢ BTW: kabuk var o nerede A68 dan esac & fi ¢
NevilleDNZ

24

Kare parantezlerin yazılması []daha kolaydır, çünkü “Multics'te yaygın olarak kullanılan” IBM 2741 terminalinden beri , sırayla ekip üyesi olarak C dilini oluşturanlardan Dennis Ritchie vardı .

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

Not yokluğunu IBM 2741 düzenini küme parantezleri!

C'de, köşeli parantezler diziler ve işaretçiler için kullanıldığından "alınır" . Dil tasarımcıları, dizilerin ve işaretçilerin kod bloklarından daha önemli olmalarını / kullanılmalarını beklerse (bu , kendi tarafında makul bir varsayım gibi geliyor , aşağıdaki kodlama stilinin tarihi bağlamında daha fazla), bu, kaşlı ayraçların "daha az önemli olacağı" anlamına geliyor "sözdizimi"

Dizilerin önemi, Ritchie'nin C Dilinin Gelişimi makalesinde oldukça belirgindir . Hatta açıkça ifade edilen "C programlarında işaretçilerin yaygınlığı" varsayımı .

... yeni dil tutarlı ve uygulanabilir (sıradışıysa) dizilerin anlambilimsel açıklamalarını korudu ... İki fikir sınıfının dilleri arasında C'nin en karakteristik özelliği : diziler ve işaretçiler arasındaki ilişki ... C, dizilerin tedavisi ... gerçek erdemlere sahiptir . İşaretçilerle diziler arasındaki ilişki sıradışı olmasına rağmen, öğrenilebilir. Dahası, dil önemli kavramları açıklamada hatırı sayılır bir güç gösterir ; örneğin, çalışma zamanında uzunluğu değişen vektörler, sadece birkaç temel kural ve konvansiyonla ...


Tarihsel bağlamı ve C dilinin yaratıldığı zamanın kodlama stilini daha iyi anlamak için, "C'nin kökeninin Unix'in gelişimine yakından bağlı olduğu" ve özellikle işletim sisteminin bir PDP'ye aktarıldığı dikkate alınmalıdır. 11 "C'nin eski bir sürümünün geliştirilmesine öncülük etti" ( kaynak tırnak ). Wikipedia'ya göre , "1972'de Unix C programlama dilinde yeniden yazıldı" .

Unix'in çeşitli eski sürümlerinin kaynak kodu çevrimiçi olarak mevcuttur, örneğin Unix Ağacı sitesinde. Orada sunulan çeşitli sürümlerden en alakalıları 1972-06 tarihli İkinci Sürüm Unix gibi görünüyor :

Unix'in ikinci baskısı, Bell Thompson’daki PDP-11 için Ken Thompson, Dennis Ritchie ve diğerleri tarafından geliştirilmiştir. First Edition'ı daha fazla sistem çağrısı ve daha fazla komutla genişletti. Bu sürüm aynı zamanda bazı komutları yazmak için kullanılan C dilinin de başlangıcını gördü ...

Zamanın tipik kodlama stili hakkında bir fikir edinmek için Second Edition Unix (V2) sayfasından C kaynak kodunu tarayabilir ve inceleyebilirsiniz .

V2 / c / ncc.c kaynak kodunda , programcının kolaylıkla köşeli parantezler yazabilmesinin çok önemli olduğu fikrini destekleyen belirgin bir örnek bulunabilir :

/* 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;

Hedeflenen pratik uygulamalarda kullanımlarına dayanarak dil sözdizimi öğelerini ifade etmek için karakter toplama pragmatik motivasyonunun, bu müthiş cevabında açıklandığı gibi Zipf Yasası ile aynı olduğuna dikkat etmek ilginçtir ...

frekans ve uzunluk arasındaki gözlenen ilişkiye Zipf Yasası denir

... yukarıdaki açıklamadaki uzunluktaki tek farkla yazım hızı yerine / generalized kullanılır.


5
Dil tasarımcılarının bu "görünür" beklentisini destekleyen herhangi bir şey var mı? Kıvrımlı ayraçların dizi bildirimlerinden çok daha yaygın olduğunu fark etmek, C de fazla programlama gerektirmez. Bu eski günlerden beri pek değişmedi - K & R'ye bir göz atın.

1
Her nasılsa bu açıklamadan şüpheliyim. Beklenenin ne olduğunu bilmiyoruz ve onlar da kolayca başka bir yolla seçmiş olabilirlerdi çünkü onlar da dizi gösterimi hakkında karar vereceklerdi. Kıvrımlı kaşlı ayraçların "daha az önemli" bir seçenek olduğunu düşünüyorlarsa bile bilmiyoruz, belki kıvrımlı ayraçları daha çok sevdiler.
thorsten müller

3
@gnat: Kare kaşlı ayraçlar, modern klavyelerde yazmak daha kolaydır, bu unix ve c ilk uygulandığı zamanki klavyeler için geçerli midir? Aynı klavyeyi kullandıklarından veya diğer klavyelerin klavyeleri gibi olacağını varsaydıklarından veya yazma hızlarının bir karakter tarafından optimize edilmeye değer olacağını düşüneceklerinden şüphem yok.
Michael Shaw,

1
Ayrıca, Zipf yasası, doğal dillerde neyin biteceğine dair bir genellemedir. C yapay olarak inşa edilmiştir, bu nedenle, C'nin tasarımcıları bilinçli bir şekilde uygulamaya karar vermedikçe, burada uygulanacağını düşünmek için hiçbir neden yoktur. Uygulanırsa, zaten tek bir karakter kadar kısa bir şeyi basitleştireceğini varsaymak için hiçbir neden yoktur.
Michael Shaw,

1
@gnat FWIW, libffi içeren 39511'i (39508 , neden iki telin kapanmadığını bilmeden) çünkü CPff kaynak kodunun (rev. 4b42d7f288c5 çünkü elimde elimde olan grep -Fobu) *.cdosyalarını anlatıyor , ancak sadece 13718 (13702) ). Bu, dizgelerde ve bağlamda bağlamda, bu soruya bağlı olmayan olayları sayar, bu nedenle, kod tabanının temsili olabileceğini göz ardı etsek bile, bu gerçekten doğru değildir (bu yanlılığın her iki yönde de olabileceğini unutmayın). Yine de, 2,8 faktörü? {{[[

1

C (ve daha sonra C ++ ve C # ), 1969'da Ken Thompson (Dennis Ritchie'nin katkılarıyla) tarafından yazılmış, kendinden önceki B'den alıştırma tarzını miras aldı.

Bu örnek, Kullanıcıların Ken Thompson tarafından B'ye gönderilmesinden kaynaklanmaktadır ( Wikipedia aracılığıyla ):

/* 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 , 1966'da Multics İşletim sistemi için Martin Richards tarafından yazılmış bir dil olan BCPL'ye dayanıyordu . B'nin destek sistemi, sadece ek parantezler ile değiştirilmiş yuvarlak parantezler kullandı ( Wikipedia'da Martin Richards'ın örneklerini yazdırın ):

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 ve sonraki dillerde "{...}" kullanılan kaşlı ayraçlar, BCPL "$ (...) $" 'da orjinal bileşik küme stilinde yapılan bir iyileştirmedir.


1
Hayır. Bundan Bob Bober'in ( en.wikipedia.org/wiki/Bob_Bemer ) bundan sorumlu olduğu anlaşılıyor - "... onların Algol 'start' ve 'end' anahtar kelimelerinin yerine kullanılabileceğini söylediniz, Daha sonra C dilinde nasıl kullanıldıklarını. " ( bobbemer.com/BRACES.HTM adresinden )
SChepurin

1
$( ... $)Biçim eşdeğerdir { ... }gibi, BCPL içinde lexer içinde ??< ... ??>eşdeğerdir { ... }değil dil - C iki stilleri arasında iyileştirme klavye donanımı bulunmaktadır.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.