c# について質問していますが、他のほとんどの言語でも同じだと思います。
式とステートメントの適切な定義と、その違いを知っている人はいますか?
c# について質問していますが、他のほとんどの言語でも同じだと思います。
式とステートメントの適切な定義と、その違いを知っている人はいますか?
式:値に評価されるもの。例: 1+2/x
ステートメント:何かを実行するコード行。例: GOTO 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) を function に渡しますcallfunc
。
式とステートメントのこの曖昧さは、すべての C 派生物 (C、C++、C#、および Java) で発生します。これらにはまだいくつかのステートメント ( などwhile
) がありますが、ほとんどすべての式をステートメントとして使用できます (C# のみの代入では、 call、increment、decrement 式をステートメントとして使用できます ( Scott Wisniewski の回答を参照)。
2 つの「構文カテゴリ」(ステートメントと式の種類の技術的な名前) があると、作業が重複する可能性があります。たとえば、C には 2 つの形式の条件文があります。
if (E) S1; else S2;
そして表現形式
E ? E1 : E2
また、存在しない重複が必要な場合もあります。たとえば、標準 C では、新しいローカル変数を宣言できるのはステートメントのみです。しかし、この機能は十分に有用であり、GNU C コンパイラーは、式でローカル変数を宣言できるようにする GNU 拡張機能を提供しています。ローカル変数も。
他の言語の設計者は、この種の重複を好まず、式が値だけでなく副作用も持つ可能性がある場合、ステートメントと式の間の構文上の区別はあまり役に立たないことに早い段階で気付きました。 . Haskell、Icon、Lisp、および ML はすべて、構文ステートメントを持たない言語です。これらには式しかありません。クラス構造のループや条件付きフォームも式と見なされ、値を持ちますが、あまり興味深いものではありません。
Cでは、「=」は実際には演算子であり、次の2つのことを行うことに注意してください。
これは、ANSIC文法からの抜粋です。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 つの大きな違いは、ステートメントを連鎖できないのに対し、式を連鎖できることです。
これはwikipediaで見つけることができますが、ステートメントには評価値がありませんが、式は何らかの値に評価されます。
したがって、ステートメントで式を使用することはできますが、その逆はできません。
一部の言語 (Lisp など、Ruby や他の多くの言語) はステートメントと式を区別しないことに注意してください。そのような言語では、すべてが式であり、他の式と連鎖させることができます。
式とステートメントの構成可能性 (チェーン可能性) の重要な違いの説明については、ジョン・バッカスのチューリング賞の論文、フォン・ノイマン スタイルからプログラミングを解放できますか?が私のお気に入りの参考文献です。.
命令型言語 (Fortran、C、Java など) は、プログラムを構造化するためのステートメントを重視し、一種の後付けとして式を持ちます。関数型言語は式を強調します。 純粋関数型言語には、ステートメントを完全に削除できるほど強力な式があります。
式は値を取得するために評価できますが、ステートメントは値を返しません (型はvoidです)。
もちろん、関数呼び出し式もステートメントと見なすことができますが、実行環境に戻り値を保持するための特別な組み込み変数がない限り、それを取得する方法はありません。
ステートメント指向言語では、すべての手続きがステートメントのリストである必要があります。おそらくすべて関数型言語である式指向言語は、式のリスト、または LISP の場合、式のリストを表す 1 つの長い S 式です。
両方の型を構成できますが、型が一致する限り、ほとんどの式を任意に構成できます。ステートメントの各タイプには、他のステートメントを構成する独自の方法があります。foreach および if ステートメントは、サブステートメントが独自のサブステートメントを許可しない限り、単一のステートメントを必要とするか、すべての従属ステートメントをステートメント ブロックに次々と入れる必要があります。
ステートメントには式を含めることもできますが、式には実際にはステートメントが含まれていません。ただし、1 つの例外は、関数を表すラムダ式であり、言語が Python の単一式ラムダのように限定されたラムダのみを許可しない限り、関数に含めることができるものはすべて含めることができます。
式ベースの言語では、すべての制御構造が値を返す (多くは NIL を返す) ため、必要なのは関数の単一の式だけです。関数内で最後に評価された式が戻り値であるため、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()
orを呼び出す TwiddleYourThumbs()
と、OK、Done、Success などのありふれた値が返されます。(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つの型の式を取り、それらを1つの型の式に結合する;
バイナリ中置演算子と考えることができます。void
void
ステートメント -> 順番に従う命令
式 -> 値を返す評価
ステートメントは基本的にステップ、またはアルゴリズムの命令に似ています。ステートメントの実行の結果は、命令ポインター (いわゆるアセンブラー) の実現化です。
式は一目で実行順序を示すものではなく、その目的は値を評価して返すことです。命令型プログラミング言語では、式の評価に順序がありますが、それは単に命令型モデルのためであり、本質ではありません。
ステートメントの例:
for
goto
return
if
(いずれも実行の行(文)を別の行に進めることを暗示しています)
式の例:
2+2
(実行という意味ではなく、評価という意味です)
statement
私は、言葉の形式論理的な意味での意味を好みます。これは、計算で 1 つ以上の変数の状態を変更し、それらの値について true または false のステートメントを作成できるようにするものです。
新しい用語や単語が導入されたり、既存の単語が「転用」されたり、ユーザーが説明している既存の確立された用語や「適切な」用語を知らない場合、コンピューティングの世界や科学全般では常に混乱が生じると思います。
これは、私が見つけた最も単純な答えの要約です。
Anders Kaseorgによる最初の回答
ステートメントは何らかのアクションを実行する完全なコード行であり、式は値に評価されるコードの任意のセクションです。
式は、演算子を使用して「水平方向」に結合してより大きな式にすることができますが、ステートメントは、次々に記述するか、ブロック構造を使用して「垂直方向」に結合することしかできません。
すべての式はステートメントとして使用できます (その効果は、式を評価し、結果の値を無視することです) が、ほとんどのステートメントは式として使用できません。
ステートメントは、すべての 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 はどちらもローカル変数を識別する単純な名前です。これらの変数が式で使用されると、変数の値が取得され、式に使用されます。
これらの概念の事実上の基礎は次のとおりです。
Expressions : インスタンスを値に評価できる構文カテゴリ。
Statement : インスタンスが式の評価に関与する可能性があり、評価の結果の値 (存在する場合) が利用可能であることが保証されていない構文カテゴリ。
初期の数十年のFORTRANの非常に初期のコンテキストに加えて、受け入れられた回答の式とステートメントの両方の定義は明らかに間違っています:
sizeof
演算子のオペランドとして使用される式は評価されません。(ところで、DMRがそのような意見を持っているかどうか思い出せないので、Cに関する資料に関するその回答に[引用が必要です]を追加したいと思います。そうではないようです。そうでなければ、Cの設計で機能の重複を維持する理由はないはずです: 特に、コンマ演算子とステートメントの比較です。)
(次の論理的根拠は、元の質問に対する直接の回答ではありませんが、ここで既に回答されていることを明確にする必要があると感じています。)
それにもかかわらず、汎用プログラミング言語で特定のカテゴリの「ステートメント」が必要かどうかは疑わしいです。
begin
Scheme など) またはモナド構造の構文糖衣に置き換えることができます。++i + ++i
(初心者に C では無意味な点を理解させる方法を考えてください。)では、なぜ声明を出すのでしょうか。とにかく、歴史はすでにめちゃくちゃです。ほとんどの言語設計者は慎重に選択していないようです。
さらに悪いことに、一部の型システム愛好家 (PL の歴史に十分に精通していない) に、型システムは操作セマンティクスに関するより本質的な規則の設計に関係する重要な事柄を持たなければならないという誤解を与えることさえあります。
真剣に、タイプに依存する推論は多くの場合それほど悪くはありませんが、この特別なものでは特に建設的ではありません. 専門家でさえ、物事を台無しにすることができます。
たとえば、誰かが、区切りのない継続の伝統的な扱いに対する中心的な議論として、適切な型付けの性質を強調しています。結論はいくぶん合理的であり、構成された関数についての洞察は問題ありませんが (ただし、本質的にはあまりにも単純すぎます_Noreturn any_of_returnable_types
)、この議論は適切ではありません。これは、実際には(C11 で) エンコードするなどの「サイド チャネル」アプローチを完全に無視しているためFalsum
です。 そして厳密に言えば、予測不可能な状態の抽象的なマシンは、「クラッシュしたコンピューター」と同じではありません。
Statements are grammatically complete sentences. Expressions are not. For example
x = 5
reads as "x gets 5." This is a complete sentence. The code
(x + 5)/9.0
reads, "x plus 5 all divided by 9.0." This is not a complete sentence. The statement
while k < 10:
print k
k += 1
is a complete sentence. Notice that the loop header is not; "while k < 10," is a subordinating clause.
私の以前の回答を改善して検証するには、プログラミング言語の用語の定義は、該当する場合はコンピュータ サイエンスの型理論から説明する必要があります。
式に Bottom タイプ以外のタイプがあります。つまり、値があります。ステートメントには、Unit または Bottom タイプがあります。
このことから、ステートメントは、値を返すことができないか、代入不可能な Unit 型の値のみを返すため、副作用を作成するときにのみプログラム内で効果を持つことができます (一部の言語では、 C のvoid
) または (Scala の場合など) は、ステートメントの遅延評価のために格納できます。
明らかに a@pragma
または a/*comment*/
には型がないため、ステートメントとは区別されます。したがって、副作用のないステートメントの唯一のタイプは、非操作です。非操作は、将来の副作用のプレースホルダーとしてのみ役立ちます。ステートメントによるその他のアクションは、副作用になります。ここでも、コンパイラ ヒント (例: @pragma
) は型を持たないため、ステートメントではありません。
最も正確には、ステートメントには「副作用」が必要であり(つまり、命令型である必要があります)、式には値の型が必要です (つまり、下位の型ではない)。
文の型は単位型ですが、ホールティング定理により単位はフィクションなので底型としましょう。
Void
正確には最下位の型ではありません (考えられるすべての型のサブタイプではありません)。完全な型システムを持たない言語に存在します。それは卑劣な発言のように聞こえるかもしれませんが、バリアンス アノテーションなどの完全性は、拡張可能なソフトウェアを作成するために重要です。
ウィキペディアがこの問題について何と言っているか見てみましょう。
https://en.wikipedia.org/wiki/Statement_(computer_science)
コンピューター プログラミングでは、ステートメントは、実行するアクションを表現する命令型プログラミング言語の最小のスタンドアロン要素です。
多くの言語 (C など) では、ステートメントと定義が区別されます。ステートメントには実行可能コードのみが含まれ、定義には識別子が宣言されますが、式は値のみに評価されます。