短絡評価、最小評価、またはMcCarthy 評価( John McCarthyにちなんで) は、一部のプログラミング言語におけるブール演算子のセマンティクスであり、最初の引数が式の値を決定するのに十分でない場合にのみ、2 番目の引数が実行または評価されます。関数の最初の引数がに評価される場合、全体の値はでなければなりません。また、関数の最初の引数が に評価される場合、全体の値は でなければなりません。
ANDfalsefalseORtruetrue
遅延評価を備えたプログラミング言語( Lisp、Perl、Haskell ) では、通常のブール演算子は短絡します。その他の言語 ( Ada、Java、Delphi ) では、短絡演算子と標準ブール演算子の両方が使用できます。排他的論理和(XOR)などの一部のブール演算では、結果を決定するために常に両方のオペランドが必要であるため、短絡は不可能です。
短絡演算子は、厳密ではないため、実際には単純な算術演算子ではなく制御構造です。副作用が重要な命令型言語用語 (特にCおよびC++ ) では、短絡演算子はシーケンス ポイントを導入します。つまり、(オプションで) 2 番目の引数を処理する前に、副作用を含めて最初の引数を完全に評価します。ALGOL 68 では、ユーザー定義の短絡演算子とプロシージャを 実現するためにプロシージャが使用されていました。
短絡演算子の使用は問題があると批判されてきた。
条件接続詞(略して「cand」と「cor」)は、一見したほど無害ではありません。たとえば、corはcandに対して分配されません。比較してください。
- (A cand B) cor Cと(A cor C) cand (B cor C) を対応させます。
¬A ∧ C の場合、2 番目の式では B が定義されている必要がありますが、最初の式では必要ありません。条件接続子はプログラムに関する形式的な推論を複雑にするため、使用を避ける方がよいでしょう。
— エドガー・W・ダイクストラ[1]
意味
短絡評価を実装するプログラミング言語では、式 は条件式と同等であり、式 はと同等です。どちらの場合でも、x は1 回だけ評価されます。
x and y if x then y else xx or yif x then x else y
上記の一般化された定義は、 と の 2 つ以上の真理値を True持つ緩く型付けされた言語に対応しており、短絡演算子は最後に評価された部分式を返す場合があります。これは、下の表では「最後の値」と呼ばれています。厳密に型付けされた言語の場合、ブール値の場合には、False式はそれぞれ と に簡略化されます。
if x then y else falseif x then true else y
優先順位
多くの言語では がより優先ANDされますが、これは短絡評価の普遍的な特性ではありません。2つの演算子が同じ優先順位を持ち、互いに左結合している例としては、 POSIXシェルのコマンドリスト構文があります。[2] : §2.9.3 OR
次の単純な左から右への評価子は、よりAND上の の優先順位を強制します。
ORcontinue
関数short-circuit-eval (演算子、値)
結果 := True
とします。 各( op、val ) は (演算子、値):
op = "AND" &&結果= False
の場合、続行します。それ以外の場合は、op = "OR" &&結果= True を
返します。それ以外の場合は、結果 := valを返します。結果を返します。
形式化
短絡論理は、副作用の有無にかかわらず、ホーアの条件に基づいて形式化されています。その結果、短絡論理から離れて、同じ評価シーケンスを持つ非短絡演算子を定義できます。[3]
一般的なプログラミング言語とスクリプト言語のサポート
以下の表を見ると、両方の引数が0、1またはブール型であっても、ビット演算子は論理演算子とまったく同じように動作しないことが多いことに注意してください。
例:
- JavaScript では、次の 3 つの式はそれぞれ、、と評価されます
false。(true & true) === (true && true)(false | false) === (false || false)(1 & 2) === (1 && 2) - PHP では、次の 3 つの式はそれぞれ、、と評価されます
false。(true & true) === (true && true)(0 | 0) === (0 || 0)(1 & 2) === (1 && 2)
- ^ ab ABAP と APL には明確なブール型がありません。
- ^ 両方の引数が型またはであり、値またはのみを取る場合、ビット演算子はブール演算子のように動作します。[4]
bool01 - ^ オーバーロードされると、演算子と演算子は先行し、任意の型を返すことができます。
&&|| - ^ これは実行時に評価される式にのみ適用されます
static if。static assert静的初期化子またはマニフェスト定数内の式では、先行評価が使用されます。 - ^ Fortran 演算子は短絡的でも積極的でもない: 言語仕様により、コンパイラが最適化の方法を選択できる。
- ^ ISO/IEC 10206:1990 Extended Pascal では短絡が許可されていますが、必須ではありません。
- ^ ab DelphiとFree Pascal はデフォルトで短絡評価を行います。これはコンパイラオプションによって変更される可能性がありますが、広く使用されているようには見えません。
- ^ Smalltalk は、 の引数がブロック (例: ) である限り、短絡セマンティクスを使用します。
and:false and: [Transcript show: 'Wont see me'] - ^ IEC 61131-3規格では、と が短絡評価を使用するかどうかは実際には定義されておらず、演算子と も定義されていません。表のエントリは、Beckhoff TwinCAT® での動作を示しています。
ANDORAND_THENOR_ELSE - ^ CASE ステートメントをサポートする BASIC言語では、固定ラベルに限定されたジャンプ テーブルではなく、条件評価システムを使用して CASE ステートメントをサポートしていました。
一般的な使用
2番目の引数の望ましくない副作用を回避する
C ベースの言語を使用した通常の例:
int denom = 0 ; if ( denom != 0 && num / denom ) { ... // num/denom の計算でゼロ除算エラーが発生しないことを保証します }
次の例を考えてみましょう。
int a = 0 ; if ( a != 0 && myfunc ( b )) { do_something (); }
この例では、短絡評価により、myfunc(b)が呼び出されないことが保証されます。これは、がfalsea != 0と評価されるためです。この機能により、2 つの便利なプログラミング構造が可能になります。
- 最初の部分式が高価な計算が必要かどうかをチェックし、そのチェックがfalseと評価された場合、2 番目の引数の高価な計算を排除できます。
- これは、最初の式が条件を保証し、それがなければ 2 番目の式が実行時エラーを引き起こす可能性がある構造を許可します。
両方とも次の C スニペットに示されています。ここでは、最小限の評価によって、ヌル ポインタの逆参照と過剰なメモリ フェッチの両方が防止されます。
bool is_first_char_valid_alpha_unsafe ( const char * p ) { return isalpha ( p [ 0 ]); // p == NULL の場合、SEGFAULT が発生する可能性が非常に高い}
bool is_first_char_valid_alpha ( const char * p ) { return p != NULL && isalpha ( p [ 0 ]); // 1) p == NULL の場合、不要な isalpha() 実行はありません。2) SEGFAULT のリスクはありません。 }
慣用的な条件構文
最小評価は演算子のセマンティック定義の一部であり、オプションの最適化ではないため、多くのコーディングイディオムでは簡潔な条件構造としてこれに依存しています。例を次に示します。
Perl の慣用句:
some_condition or die ; # some_condition が偽の場合、実行を中止しますsome_condition and die ; # some_condition が真の場合、実行を中止します
POSIXシェルの慣用句: [13]
modprobe -q some_module && echo "some_module がインストールされています" || echo "some_module がインストールされていません"
この慣用句はecho失敗できないことを前提としています。
起こりうる問題
テストされていない2番目の条件は実行されていない副作用につながる
これらの利点にもかかわらず、最小限の評価は、それが行われていることに気づかない(または忘れている)プログラマーにとって問題を引き起こす可能性があります。たとえば、コードでは
if (式A && myfunc ( b )) { do_something (); }
が実行されるmyfunc(b)かどうかに関係なく、システム リソースの割り当てなどの必要な操作を実行することになっている場合、がfalse と評価されると、は実行されず、問題が発生する可能性があります。Java などの一部のプログラミング言語には、この問題を回避するために、最小限の評価を使用する演算子と使用しない演算子の 2 つがあります。
do_something()expressionAmyfunc(b)
実行されない副作用文の問題は、適切なプログラミングスタイル、つまりブール文で副作用を使用しないことで簡単に解決できます。評価で副作用のある値を使用すると、コードが不透明になり、エラーが発生しやすくなる傾向があるためです。[14]
制約のある最適化による効率の低下
短絡は、現代の中央処理装置(CPU)の分岐予測にエラーを引き起こし、パフォーマンスを大幅に低下させる可能性があります。注目すべき例としては、レイトレーシングにおける軸に沿ったボックス交差コードで高度に最適化されたレイがあります。[説明が必要]一部のコンパイラはこのようなケースを検出してより高速なコードを生成できますが、プログラミング言語のセマンティクスによってこのような最適化が制限される場合があります。[引用が必要]
このようなケースを最適化できないコンパイラの例としては、2012年時点のJavaのHotspot仮想マシン(VM)が挙げられます。 [15]
参照
参考文献
- ^ Edsger W. Dijkstra「やや残念なやり取りについて」、EWD1009-0、1987年5月25日全文
- ^ 「シェルコマンド言語」。pubs.opengroup.org。
- ^ バーグストラ、ヤン・A.ポンセ、A.シュタウト、DJC (2010)。 「短絡論理」。arXiv : 1010.3674 [cs.LO]。
- ^ ISO/IEC 9899規格、セクション6.2.5、6.3.1.2、6.5、および7.16。
- ^ ISO/IEC 9899規格、セクション6.5.13
- ^ ISO/IEC IS 14882 ドラフト。
- ^ 「OCaml - OCaml言語」。
- ^ 「演算子 - Ruby 3.3 のドキュメント」。docs.ruby-lang.org 。 2024 年 4 月 2 日閲覧。
- ^ "std::ops - Rust". doc.rust-lang.org . 2019年2月12日閲覧。
- ^ ETSI ES 201 873-1 V4.10.1、セクション 7.1.4
- ^ 「Beckhoff Information System - 英語」。infosys.beckhoff.com 。 2021年8月16日閲覧。
- ^ 「Beckhoff Information System - 英語」。infosys.beckhoff.com 。 2021年8月16日閲覧。
- ^ 「bash で || はどういう意味ですか?」。stackexchange.com。2019年 1 月 9 日閲覧。
- ^ 「参照の透明性、明確性、展開可能性」(PDF)。Itu.dk 。 2013年8月24日閲覧。
- ^ Wasserman, Louis (2012 年 7 月 11 日)。「Java: 無条件 AND (& ではなく &) を使用する方がよいケースとはどのような場合ですか?」Stack Overflow。
