プログラミング言語、特にCでは、中括弧ではなく中括弧を使用するのはなぜですか?


96

「Cスタイル言語」の定義は、実際には「中括弧({})を使用する」まで簡略化できます。なぜその特定の文字を使用するのですか(そして[]、少なくともUSキーボードではShiftキーを必要としないなど、より合理的な文字を使用しないのはなぜですか)。

これらの中括弧からプログラマの生産性に実際の利点はありますか、または新しい言語設計者が代替手段(つまり、Pythonの背後にいる人)を探す必要がありますか?

ウィキペディアは、C 前述のブレースを使用しているが、その理由を説明していないと言っています。Cベースのプログラミング言語のリストに関するウィキペディアの記事の声明は、この構文要素がやや特別であることを示唆しています。

大まかに言って、Cファミリー言語は、Cに似たブロック構文を使用する言語です(ブロックの開始と終了のための中括弧を含む)...


35
これに答えることができる唯一の人はデニス・リッチーであり、彼は死んでいます。合理的な推測は、配列に対して[]がすでに使用されていることです。
ダークホルソップル

2
@DirkHolsoppleだから彼は理由を残しませんでしたか?ドラット。また、私が本当に興味を持っているものに対する2つのダウン投票?みんなありがとう...
SomeKittens

1
このMetaの質問でこの質問に関する議論を続けてください。
トーマスオーエンズ

2
この投稿のロックを解除しました。質問についてのコメントやメタ質問の適切性に関する議論を保管してください。
トーマスオーエンズ

5
おそらく、数学の集合表記で中括弧が使用されているため、構造体や配列などの「セット」のようなものを宣言するのではなく、配列要素のアクセスに使用するのがやや不便になっているという事実と関係があるかもしれませんPythonのような現代言語でさえ、中括弧を使用してセットと辞書を宣言します。それでは、なぜCは中括弧を使用してスコープを宣言したのですか?おそらく、設計者はBEGIN / ENDなどの既知の選択肢を好まなかったため、配列アクセス表記法([])をオーバーロードすることは、セット表記法よりも見た目が悪いと考えられていたためです。
チャールズサルビア

回答:


102

Cへの主な影響の2つは、アルゴル族の言語(アルゴル60とアルゴル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に先行する)でさらに洗練されました。

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

中かっこがbeginendアルゴルの内部および内部でショートハンドとして使用された兆候があります。

CACMで公開した256文字のカードコードにもそれらを含めたことを覚えています。Algolの「begin」および「end」キーワードの代わりに使用できることを提案したことは興味深いと思いました。後でC言語でどのように使用されたか。

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ベースのエディターでは、1つの一致するオープン/クローズの上にカーソルを置いてを押す%と、カーソルが他の一致する文字に移動します。これは、昔のCベースの言語では非常に効率的です。

より良い比較は{}begin/ endとその日の選択肢でした(水平スペースは貴重でした)。多くのWirth言語はbeginand endスタイル(Algol(前述)、pascal(多くの人が知っている)、およびModulaファミリー)に基づいていました。

この特定の言語機能を分離するものを見つけるのは困難です-せいぜい、中括弧の言語は開始言語よりもはるかに人気があり、それが一般的な構成であることを示すことができます。上記のボブ・ベマーのリンクで述べたように、中括弧は、短縮形としてプログラムしやすくするために使用されました。

Pascalが私のお気に入りのプログラミング言語ではない理由から

CとRatforのプログラマーは、「開始」と「終了」が{と}に比べてかさばると感じています。

それは言えることのすべてです-その親しみやすさと好み。


14
今ここにいる全員がBCPLを学んでいる代わりに学習しています:)
DenysSéguret13年

(1989 ISO C標準で導入された)トリグラフのため{}されている??<??>。(1995年改正により導入された)二重字がある<%%>。トライグラフは、非常に早い翻訳段階で、すべてのコンテキストで拡張されます。ダイグラフはトークンであり、文字列リテラル、文字定数、またはコメントでは展開されません。
キーストンプソン

これには1989年以前にCで何かが存在していました(日付については、初版の本を掘り下げる必要があります)。すべてのEBCDICコードページに中括弧(または角括弧)が含まれているわけではなく、初期のCコンパイラにはこれに関する規定がありました。

@NevilleDNZ BCPLは、1966年に中括弧を使用していました。Algol68がその概念を獲得した場所は、探索するものですが、BCPLはAlgo68から取得しませんでした。三項演算子は私が興味を持っているもので、Lisp(1958)から概念を借用したCPL(1963)(BCPLの前身)に遡ります。

1968:Algol68では、括弧(〜)を、ボールドシンボルブロックの開始終了の 省略形として使用できます。これらは簡単なシンボルと呼ばれ、cf wp:Algol68太字シンボル、これによりコードのブロックを式のように扱うことができます。A68も持っている簡単な Cのような速記?三項演算子: たとえばの代わりに、Cさんを。同様に、これはifcaseステートメントに適用されます。¢ところで:A68は、シェルがエサックファイを取得した場所からです¢x:=(c|s1|s2)x=c?s1|s2
NevilleDNZ

24

IBM 2741端末は「Multicsで広く使用されている」 OSであり、開発チームメンバーとして C言語クリエーターの1人であるDennis Ritchieがいた[]ため、角カッコは簡単に入力できます。

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

IBM 2741レイアウトに中括弧がないことに注意してください!

Cでは、角括弧は配列とポインターに使用されるため、「とられます」。言語設計者が配列やポインタがコードブロックよりも重要/頻繁に使用されることを期待している場合(以下のコーディングスタイルの歴史的な文脈では、合理的な仮定のように聞こえます)、それは中括弧が「あまり重要ではない」ことを意味します「構文。

配列の重要性は、リッチーの記事「C言語の開発」で明らかです。「Cプログラムでのポインターの普及」という明示的な仮定さえあります。

...新しい言語は、配列のセマンティクスについて一貫性のある実行可能な(異常場合)説明を保持していました ... クラスの言語の中でCに最も特徴的なのは、2つのアイデアです:配列とポインターの関係... C、配列の処理...には本当の美徳があります。ポインターと配列の関係は珍しいですが、学ぶことができます。さらに、この言語は、重要な概念、たとえば、実行時に長さが変化するベクトルなど、いくつかの基本的な規則と規則を使用してかなりの力発揮します...


C言語が作成された時代の歴史的背景とコーディングスタイルをさらに理解するには、「Cの起源はUnixの開発に密接に関係している」こと、特にOSをPDPに移植することを考慮する必要があります。 11 「初期バージョンのCの開発を主導」引用元ウィキペディアによると、「1972年、UnixはCプログラミング言語で書き直されました」

Unixのさまざまな古いバージョンのソースコードは、オンラインで入手できます(例:The Unix Treeサイト)。そこに提示されているさまざまなバージョンのうち、最も関連があるのは、1972-06年の第2版​​Unixです。

Unixの第2版は、ケントンプソン、デニスリッチーなどによってBell LabsのPDP-11用に開発されました。より多くのシステムコールとコマンドを追加して、First Editionを拡張しました。このエディションでは、コマンドの一部を記述するために使用されたC言語の始まりも見られました...

Second Edition Unix(V2)ページからCソースコードを参照して学習し 、当時の典型的なコーディングスタイルのアイデアを得ることができます

プログラマーが角かっこを簡単に入力できることがかなり重要だったという考え方をサポートする顕著な例は、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;

この素晴らしい答えで説明されているように、ターゲットの実用的なアプリケーションでの使用に基づいて言語構文要素を示すために文字を選択する実用的な動機がZipfの法則に似ていることに注意するのは興味深いです...

観測された頻度と長さの関係は、Zipfの法則と呼ばれます

...唯一の違いは、上記のステートメントの長さが、タイピングの速度として/一般化されていることです。


5
言語デザイナーによるこの「見かけの」期待を裏付けるものはありますか?中括弧が配列宣言よりもはるかに一般的であることに気付くのに、Cでのプログラミングはあまり必要ありません。これは昔からあまり変わっていません-K&Rをご覧ください。

1
私はどういうわけかこの説明を疑います。何が期待されているのかわからず、彼らは配列表記についても決定する人々だったので、他の方法で簡単にそれを選択できたでしょう。中かっこが「それほど重要ではない」オプションであると考えているかどうかさえわかりません。
トーステンミュラー

3
@gnat:角括弧は、最新のキーボードで入力する方が簡単です。これは、unixとcが最初に実装された頃のキーボードに当てはまりますか?同じキーボードを使用しているか、他のキーボードもキーボードと同じであると仮定したり、タイピング速度を1文字ごとに最適化する価値があると考えたりするのではないかと疑う理由はありません。
マイケルショー

1
また、Zipfの法則は、自然言語で何が起こるかについての一般化です。Cは人為的に構築されたため、Cの設計者が意図的に適用することを意図的に決定しない限り、ここで適用されると考える理由はありません。もしそれが当てはまるなら、それがすでに一文字の短いものを単純化するだろうと仮定する理由はありません。
マイケルショー

1
FWIW @gnat、grep -Fo私に伝えます*.c(それは私が手に持っているものだからREV。4b42d7f288c5)libffiを含む、CPythonのソースコードのファイルを、39511が含まれている{(39508 {、2つの括弧が閉じられていない理由を知らない)が、唯一の13718 [(13702 [)。これは、この質問に関係のない文字列やコンテキストでの出現回数をカウントするため、コードベースが代表的ではないことを無視しても、これは実際には正確ではありません(このバイアスはどちらの方向にも進む可能性があることに注意してください)。それでも、2.8倍ですか?

1

C(およびその後C ++およびC#)は、1969年にKen Thompsonによって(Dennis Ritchieの寄稿により)書かれた前身のBからブレーススタイルを継承しました。

この例は、ケントンプソンによるWikipediaを介したユーザーの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に基づいてました。BCPLは、Multicsオペレーティングシステム用に1966年にMartin Richardsによって書かれた言語です。Bのブレースシステムは、追加の文字で修正された丸ブレースのみを使用しました(Wikipedia経由でMartin Richardsによる階乗の例を印刷)。

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 "$(...)$"の元の複合ブレーススタイルに対してKen Thompsonが行った改良です。


1
いいえ。BobBemer(en.wikipedia.org/wiki/Bob_Bemer)がこれに責任があるようです-"... Algolの「begin」および「end」キーワードの代わりに使用できると提案しました。後でC言語でどのように使用されたか。」(bobbemer.com/BRACES.HTMから)
シェプリン2013

1
$( ... $)フォーマットは、と等価である{ ... }と同様に、BCPLでレクサー中??< ... ??>に等価で{ ... }ない言語- Cに2つのスタイルの間の改善は、キーボードのハードウェアです。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.