短絡評価
出典: フリー百科事典『ウィキペディア(Wikipedia)』
短絡評価(たんらくひょうか、英:short-circuit evaluation)または最小評価(さいしょうひょうか、英:minimal evaluation)とは、一部のプログラミング言語での論理演算子の意味論を示す用語であり、演算子の第一引数を評価した段階で式全体の値が定まらない場合のみ第二引数を評価する方式を意味する。例えば、ANDの第一引数を評価した結果が false
であれば、式全体は必ず false
になるし、ORの第一引数が true
であれば、式全体は必ず true
になるので、第二引数を評価するまでもない。LISPなどのプログラミング言語では、論理演算子は短絡評価である。C言語やAdaでは、短絡評価方式の演算子と標準の演算子が用意されている(C言語は普通の使い方では短絡評価しかないが、ビット演算用の演算子を論理演算に応用することで標準の論理演算が可能となる)。
短絡演算子 x Sand y
は、if x then y else false
というif文と等価であり、x Sor y
は、if x then true else y
と等価である。
典型的な短絡演算子は単なる演算子としての役割だけでなく、制御構造としての役割も持っている。ただし、ALGOL 68 は "proceduring" という機能を使ってユーザー定義の短絡評価の演算子やプロシージャを実現できる。
型付けが弱い言語では、True
と False
の値が一意に定まらない場合があり、短絡演算子は最後に評価した部分式の値を返す。従って、x Sand y
は実際には if x then x else y
( x
を2回評価するわけではない)となる。下の表ではこれを「最後に評価した値」としている。
言語 | 標準演算子 | 短絡演算子 | 値 |
---|---|---|---|
Ada | and , or |
and then , or else |
ブーリアン |
ALGOL 68 | and/&/∧ , or/∨ | andf , orf - ユーザー定義 | bool |
C , Java | & , | |
&& , || |
ブーリアン |
Javascript | & , | |
&& , || |
最後に評価した値 |
Lisp | なし | and , or |
最後に評価した値 |
Perl | & , | |
&& , and , || , or |
最後に評価した値 |
Python | & , | |
and , or |
最後に評価した値 |
Visual Basic | And , Or |
AndAlso , OrElse |
ブーリアン |
[編集] 例
例として次のC言語のコードを見てみよう。
int a = 0;
if (a && myfunc(b)) {
do_something();
}
この例では、短絡評価によって myfunc(b)
は決して呼び出されない。なぜなら a
は false と評価されるからである。この性質を利用して2種類のプログラミング技法が得られる。1つは、後半の部分式に時間のかかる処理を置き、最初の部分式でその処理が必要か否かのチェックをするようにすれば、無駄な関数呼び出しなどを減らすことができる。2つめとして、後半の部分式が実行時エラーを起こすかどうかのチェックを前半で行うというコーディングが考えられる。例えば、次のC言語コードは、短絡評価を利用してヌルポインタの参照を防いでいる。
int is_three_chars_long(const char *p) {
return (p != NULL) && (strlen(p) == 3);
}
このような利点はあるが、短絡評価であることを忘れていると問題が発生することがある。例えば、次のコードを見てみよう。
if (expressionA && myfunc(b)) {
do_something();
}
ここで myfunc(b) が常に実行されることを期待していると、expressionA が false だった場合に実行されないことになり、問題が発生する。Java などの言語では短絡評価式の演算子と普通の演算子を用意して、このような問題に対処している。
if文の条件式で使われる論理積や論理和の演算子は、if文の入れ子をコンパクトに表したものと見ることもできる。例えば、次の擬似コードでは短絡評価が行われるとする。
if (CONDITION_A && CONDITION_B) {
TRUE_BRANCH
} else {
FALSE_BRANCH
}
これは、論理積を展開することで次のようにも表せる。
if (CONDITION_A) {
if (CONDITION_B) {
BOTH_TRUE
} else {
B_FALSE
}
} else {
A_FALSE
}
ブール論理の性質から、A_FALSE と B_FALSE というコード部分が、最初の例では FALSE という部分に集約されていることに注意されたい。後者の例の方が条件によって異なる処理を記述できるため、必要に応じて使い分けることになる。両者が結局同じことをするコードであれば、共通式削除や定数伝播といったコンパイラ最適化によって、どちらも同じコードを生成する可能性がある。
短絡評価を使ってコードの一部の条件付実行を実現することもできる。以下は Perl の例である。
some_condition or die; # some_condition が false なら実行を停止
some_condition and die; # some_condition が true なら実行を停止
[編集] ALGOL 68
1968 Final Report での仕様と 1973 Revised Report での仕様は異なる。本来の ALGOL 68 には proceduring という機能があった。これは、項の値をその項を評価するプロシージャに合わせるようにすることである。proceduring は実質的には評価を「遅延」させることができる。その最も便利な利用法として、論理演算の短絡評価がある。
以下はユーザー定義演算子の例である。
op andf = (bool a, proc bool b)bool: if a then b() else a fi; ¢ b は a が true の場合のみ評価される。 ¢ op orf = (bool a, proc bool b)bool: if a then a else b() fi; ¢ b は a が false の場合のみ評価される。 ¢
従って、1973 Revised Report 以前には、プログラマは演算子(やプロシージャ)の引数の評価を逐次的にするか並行的にするかを選択可能であった。
ただし、これは予定通りには機能しなかった。多くの実装では andf/orf や andth/orel といった拡張を特別に扱って短絡評価をエミュレートしていた。