質問
C# に関して質問していますが、他のほとんどの言語でも同じだと思います。
誰かが適切な定義を持っていますか 表現 そして ステートメント 違いは何ですか?
解決
表現: 値として評価されるもの。例: 1+2/x
声明: 何かを実行するコード行。例: 後藤100
FORTRAN などの初期の汎用プログラミング言語では、その区別は明確でした。FORTRAN では、ステートメントは実行の 1 単位であり、実行することです。「行」と呼ばれなかった唯一の理由は、複数の行にまたがることがあるからです。式だけでは何もできません...それを変数に代入する必要がありました。
1 + 2 / X
FORTRAN では何も行われないため、エラーになります。その式を使って何かをする必要がありました。
X = 1 + 2 / X
FORTRAN には、今日私たちが知っているような文法はありませんでした。その考えは、Algol-60 の定義の一部として Backus-Naur Form (BNF) とともに発明されました。その時点で、 セマンティック 区別(「価値がある」対「何かをする」)は、 構文:ある種類のフレーズは式であり、別の種類はステートメントであり、パーサーはそれらを区別できます。
後の言語の設計者は区別を曖昧にしました。構文式で何かを行うことができ、値を持つ構文ステートメントも許可されました。今も残っている最も初期の人気言語の例は C です。C の設計者は、式を評価して結果を破棄しても問題が発生しないことに気づきました。C では、すべての構文式は、末尾にセミコロンを付けるだけでステートメントにすることができます。
1 + 2 / x;
まったく何も起こらないとしても、これは完全に正当な発言です。同様に、C では、式には次のものを含めることができます。 副作用—それは何かを変えることができる。
1 + 2 / callfunc(12);
なぜなら callfunc
何か役に立つことをするかもしれない。
任意の式をステートメントとして許可すると、式内で代入演算子 (=) を許可することもできます。だからこそ、C では次のようなことができます。
callfunc(x = 2);
これは式 x = 2 を評価し (2 の値を x に代入)、それ (2) を関数に渡します。 callfunc
.
式とステートメントのこの曖昧さは、すべての C 派生 (C、C++、C#、および Java) で発生しますが、これらには依然としていくつかのステートメント (次のようなもの) が含まれています。 while
) ただし、ほぼすべての式をステートメントとして使用できます (C# では、代入、呼び出し、インクリメント、およびデクリメント式のみがステートメントとして使用できます。見る スコット・ウィズニウスキーの答え).
2 つの「構文カテゴリ」 (ステートメントや式の技術名) があると、作業が重複する可能性があります。たとえば、C には条件文の 2 つの形式があります。
if (E) S1; else S2;
そして表現形式は
E ? E1 : E2
そして時々人々は 欲しい そこには存在しない重複:標準的なC言語では、新しいローカル変数を宣言できるのはステートメントだけです。 GNU Cコンパイラーは、ローカル変数も宣言できるGNU拡張機能を提供している。
他の言語の設計者はこの種の重複を好まず、式に値だけでなく副作用も含まれる可能性がある場合は、 構文的な ステートメントと式の区別はあまり役に立たないため、削除されました。Haskell、Icon、Lisp、ML はすべて、構文ステートメントを持たない言語であり、式があるだけです。クラス構造のループや条件形式も式とみなされ、値を持ちますが、あまり興味深い値ではありません。
他のヒント
上記のジョエルの答えに少し修正を加えたいと思います。
C# では、すべての式をステートメントとして使用できるわけではありません。特に、ステートメントとして使用できるのは、代入、呼び出し、インクリメント、およびデクリメント式のみです。
たとえば、C# コンパイラは次のコードに構文エラーとしてフラグを立てます。
1 + 2;
- 式とは、値を生成するものです。2+2
- ステートメントは、プログラム実行の基本的な「ブロック」の 1 つです。
C では、「=」は実際には演算子であり、次の 2 つのことを行うことに注意してください。
- 右側の部分式の値を返します。
- 右側の部分式の値を左側の変数にコピーします。
以下は ANSI C 文法からの抜粋です。C にはさまざまな種類のステートメントがあまりないことがわかります...プログラム内のステートメントの大部分は式ステートメントです。末尾にセミコロンが付いた式。
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
式は値を返すものですが、ステートメントは値を返しません。
たとえば:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
この 2 つの大きな点は、式はチェーンできるのに対し、ステートメントはチェーンできないことです。
これは次のサイトで見つけることができます ウィキペディア, ただし、式は何らかの値に評価されますが、ステートメントには評価値がありません。
したがって、式はステートメント内で使用できますが、その逆はできません。
一部の言語 (Lisp や Ruby など) ではステートメントと式を区別しないことに注意してください。このような言語では、すべてが式であり、他の式と連鎖させることができます。
式とステートメントの構成可能性 (連鎖可能性) における重要な違いの説明については、私のお気に入りの参考文献は、ジョン バッカスのチューリング賞論文です。 プログラミングはノイマン型から解放されるのでしょうか?.
命令型言語 (Fortran、C、Java など) は、プログラムを構造化するためのステートメントを重視しており、一種の後付けの式を備えています。関数型言語は式を重視します。 純粋に 関数型言語には、ステートメントを完全に削除できるほど強力な式があります。
単に:式は値に評価されますが、ステートメントは値に評価されません。
式は評価して値を取得できますが、ステートメントは値を返しません(ステートメントの型は 空所).
もちろん、関数呼び出し式もステートメントとみなすことができますが、実行環境に戻り値を保持する特別な組み込み変数がない限り、戻り値を取得する方法はありません。
ステートメント指向言語では、すべてのプロシージャがステートメントのリストである必要があります。式指向言語 (おそらくすべての関数型言語) は式のリスト、または LISP の場合は式のリストを表す 1 つの長い S 式です。
どちらのタイプも構成できますが、タイプが一致する限り、ほとんどの式は任意に構成できます。各タイプのステートメントには、他のステートメントを構成するための独自の方法があります (すべてを実行できる場合)。foreach ステートメントと if ステートメントでは、サブステートメントで独自のサブステートメントが許可されていない限り、単一のステートメントを使用するか、すべての従属ステートメントをステートメント ブロックに次々と入れる必要があります。
ステートメントには式を含めることもできますが、式には実際にはステートメントが含まれません。ただし、1 つの例外はラムダ式です。ラムダ式は関数を表すため、Python の単一式ラムダなど、言語で制限されたラムダしか許可されていない限り、関数に含めることができるものはすべて含めることができます。
式ベースの言語では、すべての制御構造が値を返すため (多くの制御構造は NIL を返す)、必要なのは関数の 1 つの式だけです。関数内で最後に評価された式が戻り値となるため、return ステートメントは必要ありません。
式ベースの言語に関するいくつかの点:
最も重要な:すべてが値を返す
すべてが式であるため、コード ブロックと式を区切るための中括弧と中括弧には違いはありません。ただし、これは字句スコープを妨げません。たとえば、ローカル変数は、その定義が含まれる式とその中に含まれるすべてのステートメントに対して定義できます。
式ベースの言語では、すべてが値を返します。最初は少し奇妙に感じるかもしれません -- とはどういう意味ですか? (FOR i = 1 TO 10 DO (print i))
戻る?
いくつかの簡単な例:
(1)
戻り値1
(1 + 1)
戻り値2
(1 == 1)
戻り値TRUE
(1 == 2)
戻り値FALSE
(IF 1 == 1 THEN 10 ELSE 5)
戻り値10
(IF 1 == 2 THEN 10 ELSE 5)
戻り値5
さらに複雑な例をいくつか示します。
- 一部の関数呼び出しなど、実際には意味のある値を返さないものもあります (副作用のみを引き起こすもの?)。電話をかける
OpenADoor(), FlushTheToilet()
またはTwiddleYourThumbs()
OK、Done、Success などのありふれた値を返します。 - リンクされていない複数の式が 1 つの大きな式内で評価される場合、大きな式で最後に評価された値が大きな式の値になります。例を挙げると、
(FOR i = 1 TO 10 DO (print i))
, 、for ループの値が「10」の場合、(print i)
式は 10 回評価され、毎回 i を文字列として返します。最終回のリターン10
, 、私たちの最終的な答え
すべてが式であるという事実により、多くのものを「インライン化」することが可能になるため、式ベースの言語を最大限に活用するには、考え方を少し変える必要があることがよくあります。
簡単な例として:
FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO ( LotsOfCode )
非式ベースの完全に有効な置き換えです。
IF MyString == "Hello, World!" THEN TempVar = 10 ELSE TempVar = 5 FOR i = 1 TO TempVar DO ( LotsOfCode )
場合によっては、式ベースのコードで許可されるレイアウトの方が、私にとってははるかに自然に感じられます。
もちろん、これは狂気につながる可能性があります。MaxScript と呼ばれる式ベースのスクリプト言語での趣味のプロジェクトの一環として、私はこのモンスター行を思いつきました。
IF FindSectionStart "rigidifiers" != 0 THEN FOR i = 1 TO (local rigidifier_array = (FOR i = (local NodeStart = FindsectionStart "rigidifiers" + 1) TO (FindSectionEnd(NodeStart) - 1) collect full_array[i])).count DO
(
LotsOfCode
)
ステートメントは式の特殊なケースであり、 void
タイプ。ステートメントを異なる方法で扱う言語の傾向は、しばしば問題を引き起こすため、それらが適切に一般化された方がよいでしょう。
たとえば、C# には非常に便利な機能があります。 Func<T1, T2, T3, TResult>
オーバーロードされた汎用デリゲートのセット。しかし、対応するものも必要です Action<T1, T2, T3>
セットも同様に設定されており、この残念な分岐に対処するために、汎用の高階プログラミングを常に複製する必要があります。
簡単な例 - 別の関数を呼び出す前に参照が null かどうかをチェックする関数:
TResult IfNotNull<TValue, TResult>(TValue value, Func<TValue, TResult> func)
where TValue : class
{
return (value == null) ? default(TValue) : func(value);
}
コンパイラは次の可能性を処理できますか? TResult
いる void
?はい。必要なのは、return の後に次の型の式が続くことを要求することだけです。 void
. 。結果として default(void)
タイプだろう void
, 、渡される関数は次の形式である必要があります。 Func<TValue, void>
(これは次と同等です Action<TValue>
).
他の多くの回答は、式のようにステートメントを連鎖させることはできないことを示唆していますが、この考えがどこから来たのかはわかりません。考えられるのは、 ;
2 項中置演算子としてステートメントの後に表示され、次のタイプの 2 つの式を取ります。 void
それらを単一の type 式に結合します。 void
.
ステートメント -> 順番に従うべき指示
式 -> 値を返す評価
ステートメントは基本的にアルゴリズムのステップまたは命令のようなもので、ステートメントの実行結果は命令ポインタ (いわゆるアセンブラでの) の実現になります。
式は一見しただけでは実行順序を意味しません。その目的は値を評価して返すことです。命令型プログラミング言語では、式の評価に順序がありますが、それは命令型モデルによるものであり、本質ではありません。
ステートメントの例:
for
goto
return
if
(これらはすべて、実行の行(ステートメント)を別の行に進めることを意味します)
式の例:
2+2
(実行という概念ではなく、評価という意味です)
ステートメントは文法的に完全な文です。表現はそうではありません。例えば
x = 5
は "xは5を得る "と読む。 これは完全な文章だ。コード
(x + 5)/9.0
は "xに5を足したものを9.0で割ったもの "となる。 これは完全な文章ではない。声明
while k < 10:
print k
k += 1
は完全な文です。ループヘッダーはそうではないことに注意してください。「while k < 10」は従属節です。
声明,
ステートメントは、すべての C# プログラムが構築される手続き的な構成要素です。ステートメントでは、ローカル変数または定数を宣言したり、メソッドを呼び出したり、オブジェクトを作成したり、変数、プロパティ、またはフィールドに値を割り当てたりすることができます。
中括弧で囲まれた一連のステートメントは、コードのブロックを形成します。メソッド本体はコード ブロックの一例です。
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
C# のステートメントには式が含まれることがよくあります。C# の式は、リテラル値、単純な名前、または演算子とそのオペランドを含むコードの断片です。
表現,
式は、単一の値、オブジェクト、メソッド、または名前空間に評価できるコードの断片です。最も単純な 2 つのタイプの式は、リテラルと単純な名前です。リテラルは名前のない定数値です。
int i = 5;
string s = "Hello World";
i と s はどちらもローカル変数を識別する単純な名前です。これらの変数が式で使用される場合、変数の値が取得され、式に使用されます。
私は次の意味の方が好きです statement
言葉の形式論理的な意味で。これは、計算で 1 つ以上の変数の状態を変更し、その値について真か偽のステートメントを作成できるようにするものです。
新しい用語や単語が導入されたり、既存の単語が「再利用」されたり、ユーザーが説明している内容に対する既存の、確立された、または「適切な」用語を知らない場合、コンピューティングの世界や科学一般では常に混乱が生じると思います。
私はここでの答えのどれにも本当に満足していません。文法を調べてみました C++ (ISO 2008). 。ただし、教訓とプログラミングの目的では、2 つの要素を区別するには答えで十分かもしれません (実際はもっと複雑に見えますが)。
ステートメントは 0 個以上の式で構成されますが、他の言語の概念を使用することもできます。これは、文法の拡張バッカス ナウル形式 (ステートメントの抜粋) です。
statement:
labeled-statement
expression-statement <-- can be zero or more expressions
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
C++ ではステートメントとみなされる他の概念も確認できます。
- 式ステートメントs は自明です (ステートメントはゼロまたは もっと 表現、文法をよく読んでください、難しいです)
case
たとえば、 ラベル付きステートメント- 選択ステートメントは
if
if/else
,case
- 反復ステートメントは
while
,do...while
,for (...)
- ジャンプステートメントは
break
,continue
,return
(式を返すことができます)、goto
- 宣言文 宣言のセットです
- トライブロック を表すステートメントです
try/catch
ブロック - 文法についてはさらに詳しく説明する必要があるかもしれません
これは式の部分を示す抜粋です。
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
- 式には代入が含まれる、または代入が含まれることがよくあります
- 条件式 (誤解を招くように聞こえます) は、演算子 (
+
,-
,*
,/
,&
,|
,&&
,||
, ...) - スロー式 - え?の
throw
句も式です
これは、私が見つけた最も単純な答えの1つを要約したものです。
最初の回答者: Anders Kaseorg
ステートメントは何らかのアクションを実行する完全なコード行であり、式は値に評価されるコードの任意のセクションです。
式は演算子を使用して「水平方向」に結合してより大きな式を作成できますが、ステートメントは次々に記述するか、ブロック構造を使用して「垂直方向」にのみ結合できます。
すべての式はステートメントとして使用できます (その結果、式が評価され、結果の値が無視されます) が、ほとんどのステートメントは式として使用できません。
私の以前の回答を改善し検証するために、プログラミング言語の用語の定義は、該当する場合にはコンピューターサイエンスの型理論から説明する必要があります。
式には Bottom 型以外の型があります。つまり、それは価値を持っています。ステートメントには Unit または Bottom タイプがあります。
このことから、ステートメントがプログラム内で何らかの影響を与えることができるのは、ステートメントが副作用を作成する場合のみであることがわかります。これは、ステートメントが値を返せないか、割り当て不可能な Unit 型の値のみを返すためです (一部の言語では)。 C void
) または (Scala など) は、ステートメントの遅延評価のために保存できます。
明らかに @pragma
または /*comment*/
型がないため、ステートメントとは区別されます。したがって、副作用のない唯一のタイプのステートメントは非操作になります。非操作は、将来の副作用に備えたプレースホルダーとしてのみ役立ちます。ステートメントによるその他のアクションは副作用となります。もう一度コンパイラのヒントを示します。 @pragma
, は型がないためステートメントではありません。
最も正確には、ステートメントには "副作用" (すなわち、 命令的であること) および式は次のとおりでなければなりません 持っている ある 価値 タイプ (すなわち、底部タイプではありません)。
の ステートメントの種類 は単位の種類ですが、ホルティング定理により単位はフィクションであるため、次のようにします。 ボトムタイプ.
Void
は正確には最下位の型ではありません (考えられるすべての型のサブタイプではありません)。それは以下の言語に存在します。 完全なサウンドタイプシステムを持っていない. 。偉そうな言い方に聞こえるかもしれませんが、完全性 差異アノテーションなど 拡張可能なソフトウェアを作成するために重要です。
この件に関してウィキペディアが何と言っているか見てみましょう。
https://en.wikipedia.org/wiki/Statement_(コンピューターサイエンス)
コンピューター プログラミングにおいて、ステートメントは、プログラムの最小の独立した要素です。 命令的な を表現するプログラミング言語 何らかのアクション 実施されます。
多くの言語 (例:C) ステートメントと定義を区別します。ステートメントには実行可能コードのみが含まれ、定義には識別子を宣言しますが、式は値のみに評価されます。