「if」および「while」と一緒に使用する場合、言語が式の周りに括弧を必要とするのはなぜですか?


67

C、Java、およびC ++のような言語はすべてで使用した場合、式全体の周りに括弧を必要としifwhileまたはswitch

if (true) {
    // Do something
}

とは対照的に

if true {
    // Do something
}

括弧が冗長であるため、これは私には奇妙に思えます。この例でtrueは、は単独の式です。括弧は、私が知っている方法でその意味を変えません。なぜこの奇妙な構文が存在し、なぜそんなに一般的ですか?気付いていないのに利点はありますか?


20
パスカルは括弧を必要としません(なぜなら、それはを必要とするからですTHEN)。
JimmyB

30
Python、Rubyはサポートしていません。
-smci

31
中括弧は単一ステートメントの本体ではオプションであるため、Cは括弧を使用すると考えています。または、おそらくより良い方法は、ブレースがifステートメントの一部ではなく、単に複合ステートメントを作成することです。
フレッドラーソン

7
興味深いことに、Goは括弧ではなく括弧を必要とします。
コス

25
質問は少しトートロジー的です。丸いマンホールが丸く覆われているのはなぜですか?兄弟全員が男性なのはなぜですか?括弧を必要とする言語がすべて括弧を必要とするのはなぜですか?丸いマンホールの蓋は、定義により丸くなっています。兄弟は定義上男性です。括弧を必要とする言語は、定義により括弧を必要とします。
エリックリッパー

回答:


155

条件が終了して分岐が開始される場所を特定する方法が必要です。それにはさまざまな方法があります。

一部の言語では、何の条件文がない全くのSmalltalk、自己、ニュースピーク、イオ、Ioke、SEPH、そしてファンシーで、例えば。条件分岐は、他の方法と同様に、通常の方法として単純に実装されます。このメソッドはブール値オブジェクトに実装され、ブール値で呼び出されます。このように、条件は単にメソッドのレシーバーであり、2つのブランチは2つの引数です(Smalltalkなど)。

aBooleanExpression ifTrue: [23] ifFalse: [42].

Javaに精通している場合、これは次と同等です。

aBooleanExpression.ifThenElse(() -> 23, () -> 42);

Lispファミリーの言語では、状況は似ています:条件は単なる通常の関数(実際にはマクロ)であり、最初の引数は条件であり、2番目と3番目の引数は分岐なので、それらは通常の関数引数であり、それらを区切るために特別な必要はありません:

(if aBooleanExpression 23 42)

一部の言語では、キーワードを区切り文字として使用しています。たとえば、アルゴル、エイダ、ベーシック、パスカル、モジューラ-2、オベロン、オベロン-2、アクティブオベロン、コンポーネントパスカル、ゾノン、モジュラ-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

Rubyでは、キーワードまたは式区切り文字(セミコロンまたは改行)を使用できます。

if a_boolean_expression then 23 else 42 end

if a_boolean_expression; 23 else 42 end

# non-idiomatic, the minimum amount of whitespace required syntactically
if a_boolean_expression
23 else 42 end

# idiomatic, although only the first newline is required syntactically
if a_boolean_expression
  23
else
  42
end

Goでは、ブランチをブロックにする必要があり、式やステートメントを許可しないため、中括弧が必須になります。したがって、括弧は必要ありませんが、必要に応じて追加できます。この点で、Perl6とRustは似ています。

if aBooleanExpression { return 23 } else { return 42 }

一部の言語では、他の非英数字を使用して条件を区切ります。たとえば、Python:

if aBooleanExpression: return 23
else: return 42

一番下の行は、条件が終了して分岐が開始される場所を伝える何らかの方法が必要です。これを行うには多くの方法がありますが、括弧はそれらの1つにすぎません。


8
非常に良い概要。
ピーター-モニカを

2
もちろん、むき出しの式がステートメントではない言語(たとえば、計算値を変数に割り当てるか、他のステートメントに渡す必要がある古いBASICのようなもの)、または常に使用できるプレフィックス演算子がない言語とにかく式の終わりと文の始まりを識別するため。IFステートメントの最後にデリミタなしで管理しているBASICバリアントを間違いなく見ることができました。
ペリアタブレアッタ

4
また、Cは70年代に設計されており、計算にコストがかかっていたため、小さな括弧を追加すると、おそらくパーサーの記述が少し簡単になります。
マチャド

4
Re:Lisp:「実際にはマクロ」。実際には、IFはSchemeおよびCLの特別な形式です(完全を期すため)。
コアダンプ

1
@Leushenko:そして、たとえば、デフォルトで怠zyなMISCでは、条件付きフォームはすべて通常の関数であり、マクロでも特殊フォームでもありません。(実際、AFAIR、MISCが持つゼロ、特殊な形式を?)
イェルクWミッターク

70

中括弧を使用する場合のみ、括弧は不要です。

if true ++ x;

たとえば、それらがないとあいまいになります。


28
@RobertHarvey- 私が知っているほとんどすべての言語で括弧必要です。確かにCとその親族。OPは、なぜそれら必要なのかを尋ねています。それは、そうしないと言語が曖昧になるからです。
テラスティン

25
私の頭のすぐ上では、括弧は必須ではありませんif:基本、アセンブリ、Python、bash / zsh、tcl、バッチ、ブレインファック、またはマシンコード。if言語が括弧に依存するように設計されている場合のみ、括弧がないと曖昧になります。
candied_orange

12
Pascal(Delphiを含む)で最も論理的で読みやすいバージョンが誰も言及していないことに驚いていif Condition then ...ます。
ウルリッヒゲルハルト

18
Goはその逆の良い例です。中括弧を{}強制的にするため、式の周りに括弧を必要としません。かっこが必要ないだけでなく、
かっこを

10
@栄子言い替えさせてください。答えの例は、構文的に曖昧ですが、意味的に曖昧ではあります(ご指摘のとおり)。しかし、構文解析フェーズはセマンティック分析の前に発生するため、パーサーはあいまいさに遭遇します。そして、情報に基づいていない推測を行うか、失敗する必要があります。(何らかの理由で)パーサーが失敗しないことを選択した場合、セマンティックアナライザーは結果のツリーを使用します。セマンティックアナライザーがパーサーにサブツリーを再解析し、構文的に曖昧な構成で別の選択をすることを望んでいるコンパイラーを見たことはありません。
セオドロスチャツィジアンナキス

21

ifステートメント内の括弧は、算術式内で使用される括弧と同じ意味を持ちません。算術式の括弧は、式をグループ化するために使用されます。ifステートメント内の括弧は、ブール式を区切るために使用されます。つまり、ブール式をifステートメントの残りの部分と区別するためです。

if文、括弧はグループ化機能を実行しない(ただし、内ifのステートメント、まだグループ演算式に括弧を使用することができる。括弧の外側のセットは、次に全体のブール式を区切るのに役立ちます)。それらを必須にすることで、コンパイラーは単純になります。コンパイラーは、常に存在する括弧を信頼できるからです。


コンパイラがどのように単純化されるかわかりません。ルール `IF '(' expression ')' statement`はほど簡単ではありません IF primary_expression statement。後者も同様に明確であることに注意してください。
-user58697

@ user58697:いいえ、後者だけがあいまいさを持ちます。このあいまいさでは、後primary_expression置演算子を式ステートメントの前置演算子と区別できません。Telastynの答えをコピーするには、if true ++ x;。また、空のステートメントが存在する場合if a & f;、空のステートメントと&条件内のバイナリ、または&ステートメントの先頭の単項のいずれかです。ただし、かっこを一致させる場合、開始点に一致するのは1つだけです(
-MSalters

@MSalters後置演算子はプライマリとして解析されませ。一次式は、の一つでありIDENTIFIERCONSTANTSTRING_LITERAL'(' expression ')'
user58697

@ user58697:特定の言語を念頭に置いているようです。また、条件が「IDENTIFIER、CONSTANT、またはSTRING_LITERAL」である場合にのみ、括弧は必要ないというルールがあるようです。それが物事を簡単にするかどうかはわかりません。
-MSalters

16

他の人がすでに部分的に指摘しているように、これは式も有効なステートメントであるという事実によるものであり、ステートメントが1つだけのブロックの場合は、括弧を削除できます。これは、以下があいまいであることを意味します。

if true
    +x;

次のように解釈できるためです。

if (true + x) {}

の代わりに:

if (true) {+x;}

いくつかの言語(Pythonなど)では、括弧を避けることができますが、終了条件マーカーがあります:

Trueの場合 + x

ただし、括弧が必要とされない言語を定義できることは間違いありません。式が有効なステートメントではない言語では、この問題は発生しません。

残念ながら、これは次のようなことを意味します。

 ++x;
 functionCall(1,2,3);

有効なステートメントではないため、式を作成せずにこのようなアクションを実行するには、奇妙な構文を導入する必要があります。これを行う簡単な方法は、次のようなマーカーを式に追加するだけです[statement]

[statement] ++x;
[statement] functionCall(1,2,3);

次のように書く必要があるので、あいまいさがなくなります。

if true
    [statement] ++x;

しかし、あなたが見ることができるように、if-condition(または:その最後)の周りに括弧を置くことは、すべての式ステートメントにそのようなマーカーを置くよりもはるかに優れているため、そのような言語は普及していません。


[statement]マーカーの使用は、考えられる最も単純な構文です。ただし、式と文のあいだにあいまいさのない、そのようなマーカーを必要としない2つの完全に異なる構文を使用できます。問題は、式またはステートメントで同じことを行うにはまったく異なる構文を使用する必要があるため、言語が非常に奇妙になることです。

明示的なマーカーせずに2つの別々の構文を持つように頭に浮かぶことの一つは、例えば、次のようになります。力Unicodeの記号を使用するステートメント(その代わりにfor、あなたは手紙のいくつかのUnicodeのバリエーションを使用したいfor)、式があることをしながら、 ASCIIのみ。


2
このような式ステートメントマーカーを使用する言語が実際に存在します。副作用について式を評価するdiscard場合は、Nimで明示的に値を指定する必要があります。ただし、これは構文上の理由ではなく、タイプセーフのために行われただけです。
アモン

@amonニース、それについて知らなかった。とにかく、マーカーは本当に必要ではないと言ったように、直感的でない構文を発明することなく、その区別を達成するための簡単な方法です。
バクリウ

1
@amon-多くのBASICバリアントには、式とステートメントの厳密な分離もあります。式は、値が実際に使用される場所でのみ許可されます(変数の割り当て、アクションを実行するPRINTなどのステートメントなど)。値の計算に使用されないプロシージャは、名前にプレフィックスを付けるキーワード(通常は「CALL」ですが、私が知っている少なくとも1つのバリアントは「PROC」を使用します)によって呼び出されます。等々。BASICは通常、IFステートメントの式の終わりを「THEN」で区切りますが、要件を落とすことができないという技術的な理由はわかりません。
ペリアタブレアッタ

1
80年代に非常に人気のある言語は、括弧、ブレース、コロン、または別のマーカーのないブロックを持ち、どこでもステートメントとして式を受け入れ、一部のステートメントは式(+ =や++などの複合演算子)として機能します。それは最悪です、コンパイラの前にダムプリプロセッサがあります(?実際の例のシンボルはPPの後の関数です)。ありません;。もちろん、継続行のマーカーが必要ですが、これはお勧めできません。harbour.github.io/doc/clc53.html#if-cmd。コンパイラは高速でシンプルです(Bison / Flexで作成)。
マニエロ

@bigown論理条件に別の構文を使用することでそれを実現します。したがって、基本的にifwhileecc の条件は他の言語で使用される一般的な式と比較して制限されます。確かに、2つ以上の構文カテゴリ(ステートメント、式、論理式、コーヒー製造式など)がある場合は、ある程度自由に取引できます。
バクリウ

10

Cファミリ言語ではこれらの括弧を必要とするのが一般的ですが、普遍的ではありません。

Perl 6ののより顕著構文変化の一つは、彼らはあなたの周りの括弧を与える必要がないように文法を変更していることであるifforと同様の声明の条件。したがって、このようなものはPerl 6で完全に有効です。

if $x == 4 {
    ...
}

そのまま

while $queue.pop {
    ...
}

ただし、それらは単なる式であるため、必要に応じて括弧を囲むことができます。その場合、C、C#、Javaなどの構文の必須部分ではなく、通常のグループ化されたものになります。

Rustの構文は、この部門のPerl 6と同様です。

if x == 4 {
    ...
}

私には、より現代のCに触発された言語の特徴は、このようなものを見て、それらを削除することについて疑問に思うようです。


あなたの答えは他の言語への洞察を提供しますが、「なぜこの奇妙な構文が存在するのか、なぜそんなに一般的なのか」とは答えません。

あなたはまったく正しいです。質問にコンテキストを追加することを本当に探していましたが、このプラットフォームでは簡単なことではありません。最終的には、質問に対する答えを提供した場合、それは「文法がそれらを持たないことに対応できなかった技術的な理由がないので」ということです。しかし、それはあまり有用な答えではありません。
マシューウォルトン

Perl 5には両方があります。BLOCKを使用した通常の構造 ifまたはループ構造の場合は、たとえば、if ( $x == 4 ) { ... }またはなどの括弧が必要ですforeach my $foo ( @bar ) { ... }。後置記法が使用される場合、括弧return unless $foo;やは省略可能です++$x while s/foo/bar/g;
シンバク

6

既存の答えがどれも出てこなかったことに驚いている側面が1つあります。

C、および多くのC派生物および類似物には、割り当ての値が割り当てられた値であるという特徴があります。この結果、値が期待される場所で割り当てを使用できるようになります。

これにより、次のように書くことができます

if (x = getValue() == 42) { ... }

または

if (x == y = 47) { ... }

または

unsigned int n = 0 /* given m == SOME_VALUE */;
while (n < m && *p1++ = *p2++) { n++; }

(これはwhile (n < m && *p1++ = *p2++ != 0) { n++; }、Cが非ゼロをtrueとして扱うため、暗黙的に扱われます;ちなみに、C標準ライブラリのstrncpy()についてだけだと思います)

あるいは

if (x = 17);

そしてそれはすべて有効です。構文的に有効なすべての組み合わせが必ずしも有用であるとは限りません(また、最新のコンパイラは一般的なエラーであるため、条件内の割り当てについて特に警告します)。

条件式の開始位置と終了位置を明確に決定する方法がなければ、このようなステートメントの解析ははるかに困難になります。

関数引数から関数名を区切るために括弧が既に使用されていたため、キーワード引数からキーワードを区切るのも自然な選択のように思えました。

確かに、同じことをするために代替構文を定義できます。ただし、そうすると、特にパーサーで複雑さが増します。パーサーは、ほぼ同じことを行うために2つの異なる構文セットを処理する必要があります。Cが設計されていた頃は、計算能力(数値処理能力、作業メモリ、ストレージ容量の両方の面で)は非常に限られていた。読みやすさをほとんどまたはまったく犠牲にすることなく複雑さを軽減したものは、ほぼ間違いなく歓迎すべき変化でした。

括弧を使用することは、今日では少し古風に思えるかもしれませんが、言語にある程度精通している人にとっては、同じことを表現できる他の構文と比べて読みやすさが損なわれます。


5

その理由は主に歴史です。

最初のCコンパイラーが作成された時点では、コンピューターは非常に限られたRAM、CPU、およびコンパイラーを持っています。したがって、複雑なルールをコンパイラに実装するにはコストがかかりました。C ++、C#、Javaなどはすべて、Cプログラマーが簡単に習得できるように設計されているため、「不要な」変更は行われていません。

'c like'言語では、条件(if, while, etc)に明示的なblockオフコードは不要で、単純なステートメントを使用できます。

if (a == d) doIt()

またはcompound statement、inを使用してステートメントを組み合わせてaにすることができます{}

私たちはコンパイラが私たちが犯したエラーを見つけて、私たちが理解できるエラーメッセージとして与えるのが好きです。


3

JavaとC ++は両方とも、Cが非常に人気のあるプログラミング言語になった後に開発されました。これらの各言語の設計における考慮事項の1つは、Cプログラマーにアピールし、それらのプログラマーに新しい言語の使用を促すことでした。(私は彼らが成功したCプログラマーの一人でした。)C ++はさらに(ほとんど)Cコードと交換可能になるように設計されました。これらの目標をサポートするために、C ++とJavaの両方が条件を囲む括弧を含め、Cの構文の多くを採用したifwhileswitch声明。

したがって、これらのすべての言語がこれらのステートメントの条件の周りに括弧を必要とする理由は、Cがそうであるためであり、問​​題は実際にはCがそれらの括弧を必要とする理由だけです。

C言語の起源は、開発の主要な著者の1人であるデニスリッチー(この開発主要な著者 と言う人もいます)によってこの記事で説明され ています。その記事で述べたように、Cは元々1970年代初期にメインメモリ内のスペースが非常に限られているコンピューター用のシステムプログラミング言語として開発されました。アセンブリ言語よりも高いレベルの言語を使用することが望まれましたが、使用可能なリソースを考慮すると、言語の解析の容易さも重要でした。括弧が必要なため、条件コードを比較的簡単に識別できます。

より少ない文字を使用してプログラムを作成できることは利点と見なされ、2つの括弧THENはその時点でFORTRANおよび他の高水準言語で使用されていたキーワードよりもスペースを取りません。実際、括弧はスペースを記号の区切り文字としても置き換えることができるため、のif(a==b)4文字全体が短くなりましたIF a==b THEN

いずれにせよ、人間がCで書かれたプログラムをどれだけ簡単に読み書きできるか、Cで書かれたプログラムをコンパイラーがどれだけ簡単に解析してコンパイルできるか、何キロバイト(!)プログラムソースとコンパイラ自体の両方に必要です。そして、の条件を囲む括弧ifwhileおよびswitch ステートメントは、人々がCの設計でそのバランスを取ることを選んだ方法でした

他のいくつかの回答で明らかなように、Cが開発された特定の状況を取り上げると、さまざまなプログラミング言語の条件にすべての種類の代替形式の構文が使用されています。したがって、括弧は実際には、歴史の特定の時間に特定の制約の下で少数の人々によって下された設計上の決定に帰着します。


C ++が「これらのプログラマーに新しい言語を使うように仕向ける」ように設計されたと言っても過言ではありません。Cのクラスを覚えていますか?
CVn

@MichaelKjörling確かに、Java開発者は「懇願」についてもっと明確に言っていました。しかし、リンクされた記事は、StroustrupがCを言語の基礎として開始することを選んだ理由の1つとして、C が広く使用されていることに言及していることに注意してくださいこれがCの近くに留まる動機を与えた1つの方法は、既存のコードを簡単に適合させることができた(既に述べたように)が、既存のコーダーも容易に適合できるためです。
デビッドK

@MichaelKjörling私の答えの元の文言は、「ウーイング」が実際よりも言語設計の大きな要因であると示唆したと思います。私は答えを編集して、それが言語設計を考慮した1つのものに過ぎないことを明確にしようと試みました。
デビッドK

3

ここでの多くは、括弧なしでは構文があいまいであり、これが何らかの形で悪い、または不可能な状況になることを黙って暗示していると考えています。

実際、言語にはあいまいさを処理する方法がたくさんあります。演算子の優先順位は、このトピックの1つのインスタンスにすぎません。

いいえ、括弧の理由はあいまいさではありません。条件の括弧を必要としない(したがってオプションにする)Cのバージョンを簡単に作成でき、すべての場合で有効なコードを作成できると思います。の例はif a ++ b;if (a) ++b;またはに相当すると解釈できますがif (a++) b;、より適切と思われるものは何でも。

Dennis Ritchieが()を必須にすることを選択した理由(したがって、多くの派生言語でこのミームを作成した理由)は、言語的な問題です。条件が命令ではなく表現であり、思考の父であると明確に述べるという考えだと思います。

そして実際、Cはワンパスパーサーを使用して解析できるように設計されました。条件を囲む必須の括弧付きの構文を使用すると、この側面がサポートされます。


私の答えには下票があります。気に入らなかった点をコメントで説明してください。それで改善できるかもしれません。
-Alfe

0

ifFortran、Cobol、PL / 1、Algol、Algo-68、Pascal、Modula、XPL、PL / M、MPLなど、thenキーワードを含む他の言語では、条件を囲む括弧は不要です。以下からthenを区切るのに役立ちconditionますstatement

Cなどの閉じ括弧はとして機能しthen、開き括弧は正式に冗長です。

上記の説明は、伝統的に解析された言語に適用されます。


Fortran では、構造体を含むIFのすべてのバージョンに括弧必要です。
Netch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.