「Cスタイル言語」の定義は、「中かっこを使用する({}
)。 "なぜその特定の文字を使用するのですか(そして[]
、少なくともUSキーボードではShiftキーを必要としません)?
これらの中括弧から来るプログラマの生産性に実際の利点はありますか、または新しい言語設計者が代替手段(つまり、Pythonの背後にいる人)を探す必要がありますか?
Wikipedia は、Cusesが中括弧を使用したことを示していますが、その理由はわかりません。 Cベースのプログラミング言語のリスト に関するWikipediaの記事のステートメントは、この構文要素がやや特殊であることを示唆しています。
大まかに言えば、Cファミリー言語は、Cのようなブロック構文を使用する言語です(中括弧ブロックを開始および終了するには)...
Cへの主な影響の2つは、言語のALGOLファミリー(ALGOL 60およびALGOL 68)とBCPL(Cがその名を冠した)です。
BCPLは最初の中括弧プログラミング言語であり、中括弧は構文の変更後も存続し、プログラムのソースコードステートメントを表す一般的な手段になっています。実際には、その日の限られたキーボードでは、ソースプログラムはしばしば記号{および}の代わりにシーケンス$(および$)を使用していました。 Cで取り上げられなかったBCPLの単一行 '//'コメントは、C++で、そして後にC99で再び登場しました。
から http://www.princeton.edu/~achaney/tmve/wiki100k/docs/BCPL.html
BCPLは、後の言語の設計で非常に一般的な要素となったいくつかの革新を導入および実装しました。したがって、これは最初の波括弧プログラミング言語(ブロック区切りとして{}を使用する言語)であり、//を使用してインラインコメントをマークする最初の言語でした。
から http://progopedia.com/language/bcpl/
BCPL内では、中括弧が頻繁に表示されますが、常に表示されるとは限りません。これは当時のキーボードの制限でした。文字$(
および$)
は、辞書的に{
および}
と同等でした。 ダイグラフとトリグラフ はCで維持されていました(中括弧の置換用の別のセット-??<
と??>
)。
中括弧の使用は [〜#〜] b [〜#〜] (Cの前)でさらに洗練されました。
Ken ThompsonによるUsers 'Reference to 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');
}
中括弧がALGOL内のbegin
とend
の省略形として使用されていたことが示されています。
CACMで公開した256文字のカードコードにもそれらを含めたのを覚えています。これは、ALGOLの「begin」および「end」キーワードの代わりに使用できると提案したのは興味深いことです。それらが後でC言語でどのように使用されたか。
から http://www.bobbemer.com/BRACES.HTM
角括弧の使用(質問で推奨されている置換として)はさらに遡ります。前述のように、ALGOLファミリーはCに影響を与えました。ALGOL60および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言語は、begin
およびend
スタイル(ALGOL(上記)、Pascal(多くの人が精通している)、およびModulaファミリー)に基づいていました。
私はこの特定の言語機能を分離するものを見つけるのが困難です-せいぜい私ができることは、中かっこ言語が開始終了言語よりもはるかに人気があり、それが一般的な構成であることを示すことです。上記のBob Bemerのリンクで述べたように、中かっこは、省略形としてプログラミングしやすくするために使用されました。
From Pascalが私のお気に入りのプログラミング言語ではない理由
CとRatforのプログラマーは、{と}に比べて、 'begin'と 'end'がかさばると考えています。
それは言うことができることすべてについてです-その親しみやすさと好み。
角括弧[]
は、 IBM 2741 ターミナルが "Multicsで広く使用されていた" OS以来、入力が簡単になりました。次に、C言語クリエーターの1人であるデニスリッチー が開発チームメンバー として参加しました。
IBM 2741レイアウトで中括弧が存在しないことに注意してください!
Cでは、角かっこは 配列とポインター に使用されるため、「中かっこ」が使用されます。言語設計者が配列とポインタの重要性を期待した場合/コードブロックよりも頻繁に使用した場合(これは 合理的な仮定 のように聞こえますが、以下のコーディングスタイルの歴史的なコンテキストについてより詳しく説明しています)、つまり、中かっこは「重要度の低い」構文になります。
配列の重要性は、リッチーの記事 The Development of the C Language でかなり明白です。 「Cプログラムでのポインターの普及」の明示的な仮定さえあります。
...新しい言語は、配列のセマンティクスの一貫した実用的な(異常な場合)説明を保持しています... 2つのアイデアはCクラスの言語間:配列とポインタの関係... Cのもう1つの特徴的な機能である配列の処理...には、実際の利点があります。ポインタと配列の関係は珍しいですが、それは学ぶことができます。さらに、この言語はかなりの能力を示し、重要な概念、たとえば、実行時に長さが変化するベクトルにいくつかの基本的な規則と規則のみを記述します...
C言語が作成された当時の歴史的文脈とコーディングスタイルをさらに理解するには、「Cの起源はUnixの開発に密接に関連している」、特に、PDP-11へのOSの移植 "は、Cの初期バージョンの開発につながりました"( quotes source )。 Wikipedia 、 "によると、1972年に、UnixはCプログラミング言語で書き直されました"。
Unixのさまざまな古いバージョンのソースコードはオンラインで入手できます(例: The Unix Tree site)。そこに提示されているさまざまなバージョンのうち、最も関連があるのは Second Edition Unix dated 1972-06のようです。
Unixの第2版は、Bell LabsのPDP-11用に、ケントンプソン、デニスリッチーなどによって開発されました。それはより多くのシステムコールとより多くのコマンドで初版を拡張しました。このエディションはまた、いくつかのコマンドを記述するために使用されたC言語の始まりを見ました...
Second Edition Unix(V2)page から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の法則 と呼ばれます
...唯一の違いは、上記のステートメントのlengthが、タイピングの速度として一般化された/に置き換えられることです。
C(およびその後のC++とC#)は、その前身であるブレーシングスタイル [〜#〜] b [〜#〜] を継承しました。
この例は、Ken Thompsonによる「User's Reference to B」( 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自体も [〜#〜] bcpl [〜#〜] に基づいていました。これは、Martin RichardsがMulticsオペレーティングシステム用に1966年に記述した言語です。 Bのブレースシステムは、丸いブレースのみを使用し、追加の文字で変更しました(Martin RichardsによるPrint factorialsの例 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)
Bおよび後続の言語「{...}」で使用される中括弧は、BCPL「$(...)$」の元の複合ブレーススタイルをKen Thompsonが改善したものです。