短絡評価

短絡評価最小評価、またはMcCarthy 評価( John McCarthyにちなんで) は、一部のプログラミング言語におけるブール演算子のセマンティクスであり、最初の引数が式の値を判定するのに十分でない場合にのみ、2 番目の引数が実行または評価されます。つまり、関数の最初の引数がと評価される場合、全体の値は でなければなりません。また、関数の最初の引数が と評価される場合、全体の値は でなければなりませんANDfalsefalseORtruetrue

遅延評価を行うプログラミング言語LispPerlHaskell)では、通常のブール演算子はショートサーキット(短絡)となります。その他の言語(AdaJavaDelphi)では、ショートサーキット演算子と標準ブール演算子の両方が利用可能です。排他的論理和(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 の定義を必要としますが、1番目の式は必要としません。このように条件接続詞はプログラムの形式的推論を複雑にするため、使用を避ける方がよいでしょう。

意味

短絡評価を実装するプログラミング言語では、式 は条件式と等価であり、式は と等価です。どちらの場合も、xは一度だけ評価されます。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

次の単純な左から右への評価子は、 よりも が優先されることを強制ANDORますcontinue

関数short-circuit-eval (演算子) let  result  := True for each ( opval ) in (演算子): if  op = "AND" && result = False continue  else if  op = "OR" && result = True return  result  else  result  := val  return  result

形式化

短絡論理は、副作用の有無にかかわらず、ホーアの条件に基づいて形式化されている。その結果、短絡論理から外れた非短絡演算子も、同じ評価シーケンスを持つように定義できる。[3]

ビット演算子との比較

&は多くのプログラミング言語で使われるビット演算子|です。主な違いは、ビット演算子は2進数の個々のビットに対して演算を行うのに対し、条件演算子は論理演算に対して演算を行う点です。さらに、ビット演算子の両側の式は常に評価されます。JavaC#などの一部の言語では、ブールのオペランドに対してこれらの演算子を使用することで、両辺を強制的に評価することができます。

if (式1 ||式2 ||式3 )     

式 1 が true の場合、式 2 と 3 はチェックされません。

if (式1 |式2 |式3 )     

これは、式 1 が true であっても、式 2 と 3 をチェックします。

短絡演算子は不要な計算を回避することで実行時間を短縮できます。また、式1でオブジェクトの有効性を確認する際に発生するnull例外を回避することもできます。

一般的なプログラミング言語とスクリプト言語のサポート

以下の表は、一般的なプログラミング言語と、論理積 AND論理和 ORの基本的なブール演算子に限定されています。ビット演算子は、先行ブール演算子として使用でき、戻り値の型が同じ言語にのみ示されています。

他にも短絡演算子は存在します。例えば、三項条件演算子( CC++JavaPHP )、( ALGOLHaskellKotlinRust )、( Python ) で使用できます。三項条件演算子#使用方法を参照してくださいcond ? e1 : e2if cond then e1 else e2e1 if cond else e2

一般的な言語におけるブール演算子
言語熱心なオペレーター短絡演算子結果タイプ
エイダandorand thenor elseブール値
アルゴル68そして、&、∧ ; または、∨andf 、 orf (どちらもユーザー定義)ブール値
APL:AndIf:OrIfブール値
awkなし&&||ブール値
CObjective-C&|[あ]&&, ||[5]整数
C++ [b]なし&&, ||[6]ブール値
C#&|&&||ブール値
D [c]&|&&||ブール値
エッフェルandorand thenor elseブール値
アーランandorandalsoorelseブール値
Fortran [d].and..or..and..or.ブール値
GoHaskellOCaml [e]なし&&||ブール値
JavaRSwift&|&&||[f]ブール値
JavaScriptなし&&||最後の値
ジュリアなし&&||最後の値
コトリンandor&&||ブール値
LispLua [e]Schemeなしandor最後の値
MATLAB [g]&|&、、、、|&&||ブール値
おたふく風邪(M)&!なし数値
モジュラ2なしANDORブール値
パスカルandor[こんにちは]and_thenor_else[私]ブール値
パール&|&&、、、、and||or最後の値
PHPなし&&、、、、and||orブール値
POSIXシェルBashなし&&||数値(終了コード)
PowerShellスクリプト言語なし-and-orブール値
パイソン&|andor最後の値
ルビー&|&&、、、and[ 9|| ]or最後の値
さび&|&&, ||[10]ブール値
雑談&|and:or:ブール値
標準ML未知andalsoorelseブール値
ビジュアルベーシック.NETAndOrAndAlsoOrElseブール値
Visual BasicVisual Basic for Applications (VBA)AndOrなし数値
  1. ^ ビット演算子は、両方の引数が型boolまたはで値0またはのみを取る場合、ブール演算子のように動作します1[4]
  2. ^ オーバーロードされる、演算子と演算子は先行型となり、任意の型を返すことができます。&&||
  3. ^ これは実行時に評価される式にのみ適用されますstatic ifstatic assert静的初期化子またはマニフェスト定数内の式では、先行評価が使用されます。
  4. ^ Fortran の演算子は短絡的でも積極的でもない: 言語仕様により、コンパイラが最適化の方法を選択できる。
  5. ^ ab lua および OCaml では、ビット演算子&|(OCaml landlor) は整数に制限されており、ブール値では使用できません。
  6. ^ Javaでは、これらを合わせて?…:「条件演算子」と呼ばれることもあります。[7]
  7. ^ 演算子は、または&に続く文で使用すると短絡演算子のように動作します[8]ifwhile
  8. ^ ISO/IEC 10206:1990 Extended Pascalでは短絡が許可されますが、必須ではありません。
  9. ^ ab DelphiFree Pascalはデフォルトで短絡評価を行います。これはコンパイラオプションで変更可能ですが、広く使用されているようには見えません。
  10. ^ Smalltalk は、 の引数がブロック (例: ) である限り、短絡セマンティクスを使用しますand:false and: [Transcript show: 'Wont see me']

一般的な使用

2番目の引数の望ましくない副作用を回避する

C ベースの言語を使用した通常の例:

int denom = 0 ; if ( denom != 0 && num / denom ) { ... // num/denom の計算でゼロ除算エラーが発生しないことを保証します }             

次の例を考えてみましょう。

int a = 0 ; if ( a != 0 && myfunc ( b )) { doSomething (); }          

この例では、短絡評価によってmyfunc(b)が呼び出されないことが保証されます。これは、がfalsea != 0と評価されるためです。この機能により、2つの便利なプログラミング構造が可能になります。

  1. 最初の部分式で高価な計算が必要かどうかをチェックし、そのチェックがfalseと評価された場合、2 番目の引数で高価な計算を排除できます。
  2. これは、最初の式が条件を保証し、それがなければ 2 番目の式が実行時エラーを引き起こす可能性がある構造を許可します

両方とも次の C スニペットに示されています。ここでは、最小限の評価によって、ヌル ポインタの逆参照と過剰なメモリ フェッチの両方が防止されます。

bool isFirstCharValidAlphaUnsafe ( const char * p ) { return isalpha ( p [ 0 ]); // p == NULL の場合、SEGFAULT が発生する可能性が非常に高い}       bool isFirstCharValidAlpha ( 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シェルの慣用句: [11]

modprobe  -q  some_module && echo "some_module がインストールされています" || echo "some_module がインストールされていません"      

この慣用句はecho失敗できないことを前提としています。

起こりうる問題

テストされていない2番目の条件は実行されていない副作用につながる

これらの利点にもかかわらず、最小限の評価は、それが行われていることに気づかない(あるいは忘れる)プログラマにとって問題を引き起こす可能性があります。例えば、以下のコードでは

if (式A && myFunc ( b )) { doSomething (); }     

、実行されるかどうかに関わらず、システムリソースの割り当てなど、myFunc(b)何らかの必要な操作を実行するはずであり、評価結果がfalseの場合、実行されず、問題が発生する可能性があります。Javaなどの一部のプログラミング言語ではこの問題を回避するために、最小評価を行う演算子と行わない演算子の2つが用意されています。doSomething()expressionAmyFunc(b)

副作用文が実行されない問題は、適切なプログラミングスタイル、つまりブール文で副作用を使用しないことで簡単に解決できます。評価で副作用のある値を使用すると、コードが不透明になり、エラーが発生しやすくなる傾向があるためです。[12]

制約のある最適化による効率の低下

短絡は、現代の中央処理装置(CPU)における分岐予測エラーにつながり、パフォーマンスを劇的に低下させる可能性があります。注目すべき例としては、レイトレーシングにおける軸に沿ったボックス交差のコードで高度に最適化されたレイが挙げられます。[要説明]一部のコンパイラはこのようなケースを検出し、より高速なコードを出力できますが、プログラミング言語のセマンティクスによってこのような最適化が制限される場合があります。[要出典]

このようなケースに最適化できないコンパイラの例として、2012年時点のJavaHotspot仮想マシン(VM)が挙げられます。[13]

参照

参考文献

  1. ^ Edsger W. Dijkstra「やや残念なやり取りについて」EWD1009-0、1987年5月25日全文
  2. ^ 「シェルコマンド言語」。pubs.opengroup.org
  3. ^ バーグストラ、ヤン・A.ポンセ、A.シュタウト、DJC (2010)。 「短絡論理」。arXiv : 1010.3674 [cs.LO]。
  4. ^ ISO/IEC 9899規格、セクション6.2.5、6.3.1.2、6.5、および7.16。
  5. ^ ISO/IEC 9899規格、セクション6.5.13
  6. ^ ISO/IEC IS 14882 ドラフト。
  7. ^ 「等価演算子、関係演算子、条件演算子(Java™チュートリアル > Java言語の学習 > 言語の基礎)」。docs.oracle.com 。 2019年4月29日閲覧
  8. ^ "and, &". MathWorks ヘルプセンター. 2025年2月2日閲覧。
  9. ^ "operators - Ruby 3.3のドキュメント". docs.ruby-lang.org . 2024年4月2日閲覧
  10. ^ "std::ops - Rust". doc.rust-lang.org . 2019年2月12日閲覧
  11. ^ 「bashで||はどういう意味ですか?」stackexchange.com . 2019年1月9日閲覧
  12. ^ 「参照の透明性、明確性、展開可能性」(PDF) Itu.dk . 2013年8月24日閲覧
  13. ^ Wasserman, Louis (2012年7月11日). 「Java: 無条件AND(&&ではなく&)を使用する方が良いケースとはどのような場合か」. Stack Overflow .
「https://en.wikipedia.org/w/index.php?title=Short-circuit_evaluation&oldid=1320973435」より取得