短絡評価、最小評価、またはマッカーシー評価(ジョン・マッカーシーにちなんで)は、一部のプログラミング言語における一部のブール演算子のセマンティクスであり、第 1 引数だけでは式の値を決定するのに十分でない場合にのみ、第 2 引数が実行または評価されます。関数の第 1 引数がと評価される場合、全体の値は でなければなりません。また、関数の第 1 引数が と評価される場合、全体の値は でなければなりません。ANDfalsefalseORtruetrue
遅延評価を採用しているプログラミング言語(Lisp、Perl、Haskellなど)では、通常のブール演算子は短絡評価を行います。その他の言語(Ada、Java、Delphiなど)では、短絡評価と標準のブール演算子の両方が利用可能です。排他的論理和(XOR)のような一部のブール演算では、結果を決定するために両方のオペランドが必要となるため、短絡評価は不可能です。
短絡評価演算子は、厳密ではないため、実際には単純な算術演算子というより制御構造です。命令型言語(特にCとC++)では、副作用が重要なため、短絡評価演算子はシーケンスポイントを導入します。つまり、最初の引数を副作用も含めて完全に評価してから、(オプションで)2番目の引数を処理します。ALGOL 68では、プロシージャを使用してユーザー定義の短絡評価演算子とプロシージャを実現していました。
短絡演算子の使用は問題があると批判されている。
条件接続詞「cand」と「cor」は、一見無害そうに見えるかもしれないが、実際はそうではない。例えば、corはcandに対して分配法則を満たさない。比較してみよう。
- (AとB)またはCと(AとC)または(BとC)
¬A ∧ C の場合、2 番目の式では B が定義されている必要がありますが、1 番目の式では必要ありません。このように条件結合子はプログラムに関する形式的な推論を複雑にするため、使用を避けるのが望ましいです。
短絡評価を実装するプログラミング言語では、式は条件式と等価であり、式はと等価です。どちらの場合も、xは一度だけ評価されます。x and yif 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.3OR
以下の単純な左から右への評価器は、 の優先順位ANDをORによって強制しますcontinue。
function short-circuit-eval ( operators , values ) let result := True for each ( op , val ) in ( operators , values ): if op = "AND" && result = False continue else if op = "OR" && result = True return result else result := val return result
副作用の有無にかかわらず、短絡論理はホーアの条件に基づいて形式化されている。その結果、短絡論理から非短絡演算子を定義して、同じ評価シーケンスを持つことができる。[ 3 ]
&と は、多くのプログラミング言語で使用されるビット演算子|です。主な違いは、ビット演算子はバイナリ数値の個々のビットに対して作用するのに対し、条件演算子は論理演算に対して作用する点です。さらに、ビット演算子の両側の式は常に評価されます。JavaやC#など一部の言語では、ブール演算子に使用して両側を強制的に評価することができます。
if ( expression1 || expression2 || expression3 )式1が真の場合、式2と式3はチェックされません。
if ( expression1 | expression2 | expression3 )これは、式1が真であっても、式2と式3をチェックします。
短絡評価演算子は、不要な計算を回避することで実行時間を短縮できます。また、式1がオブジェクトの有効性をチェックする際に発生するヌル例外も回避できます。
以下の表は、一般的なプログラミング言語と、論理積ANDおよび論理和ORのための基本的なブール演算子に限定されています。ビット演算子は、即時ブール演算子として使用でき、かつ戻り値の型が同じ言語についてのみ表示されています。
多くのプログラミング言語には、厳密でない演算子が他にもあり、特に三項条件式のように構文が異なる条件式が挙げられます。エルビス演算子は、最後の値を返す短絡評価の構文です。AND
bool、または値0または1。 [ 4 ]&&||static if。static assert静的初期化子またはマニフェスト定数内の式は、即時評価を使用します。&、|(OCamlland、lor)は整数に制限されており、ブール値では使用できません。?…:「条件演算子」と呼ばれることもあります。 [ 7 ]&の後に続く文で使用される場合、短絡演算子のように動作します。 [ 8 ]ifwhileand:falseand: [Transcriptshow:'Wont see me']boolean。bitこれらandの演算(からに適用されるとorが常に評価されます。nandnorstd_logicieee.std_logic_1164一般的な例として、C言語をベースとした言語を使用した場合:
int denom = 0 ; if ( denom != 0 && num / denom ) { ... // num / denom の計算でゼロ除算エラーが発生しないようにする }次の例を考えてみましょう。
int a = 0 ; if ( a != 0 && myfunc ( b )) { doSomething (); }この例では、短絡評価により、myfunc(b)が呼び出されないことが保証されます。これは、がfalseと評価されるためです。この機能により、2つの便利なプログラミング構造が可能になります。a != 0
以下のC言語のコードスニペットでは、どちらも最小限の評価によってヌルポインタの逆参照と過剰なメモリフェッチの両方が防止されている例を示しています。
bool isFirstCharValidAlphaUnsafe ( const char * p ) { return isalpha ( p [ 0 ]); // p == NULL の場合、セグメンテーション違反が発生する可能性が非常に高い}bool isFirstCharValidAlpha ( const char * p ) { return p != NULL && isalpha ( p [ 0 ]); // 1) p == NULL の場合、不要な isalpha() の実行を回避、2) セグメンテーション違反のリスクを回避}最小評価は演算子の意味定義の一部であり、オプションの最適化ではないため、多くのコーディング慣習では、簡潔な条件構造としてこれを利用しています。例としては、以下のようなものがあります。
Perlの慣用表現:
some_condition or die ; # some_condition が false の場合は実行を中止するsome_condition and die ; # some_condition が true の場合は実行を中止するmodprobe -q some_module && echo "some_module がインストールされました" || echo "some_module がインストールされていません"この慣用句は、それがecho失敗するはずがないという前提に基づいている。
これらの利点にもかかわらず、最小限の評価は、それが行われていることに気づかない(または忘れる)プログラマーにとって問題を引き起こす可能性があります。たとえば、コードでは
if ( expressionA && myFunc ( b )) { doSomething (); }システムリソースの割り当てなど、myFunc(b)何らかの必須操作を実行する必要があるにもかかわらず、が偽と評価された場合、は実行されず、問題が発生する可能性があります。Javaなどの一部のプログラミング言語では、この問題を回避するために、最小限の評価を行う演算子と行わない演算子の2種類が用意されています。doSomething()expressionAmyFunc(b)
副作用のないステートメントの問題は、適切なプログラミングスタイル、つまりブールステートメントで副作用を使用しないことで簡単に解決できます。副作用のある値を評価に使用すると、一般的にコードが不透明になり、エラーが発生しやすくなるためです。[ 12 ]
短絡は、最新の中央処理装置(CPU)での分岐予測のエラーにつながり、パフォーマンスを劇的に低下させる可能性があります。顕著な例としては、レイ トレーシングにおける高度に最適化された光線の交差が挙げられます。[ 13 ]一部のコンパイラはこのようなケースを検出してより高速なコードを出力できますが、プログラミング言語のセマンティクスがこのような最適化を制限する場合があります。
このようなケースに最適化できないコンパイラの例として、2012年時点のJavaのHotspot仮想マシン(VM)が挙げられる。 [ 14 ]