【ゲーム開発のためのC#入門講座・おまけ編】論理演算子の評価方法の違いを理解しよう【#3】

9.0_C#おまけ編

論理演算子の記号は何で二個なんだろう?

条件式で使う論理演算子「&&」と「||」は同じ記号を二つ続けていますよね。

実は二つ続きじゃなくてもほぼ同等の意味で使うことができます

// &の2個バージョンと1個バージョン
Console.WriteLine(0 == 0 && 0 == 1);
Console.WriteLine(0 == 0 & 0 == 1);
​
// |の2個バージョンと1個バージョン
Console.WriteLine(0 == 0 || 0 == 1);
Console.WriteLine(0 == 0 | 0 == 1);
コンソール画面

False
False
True
True

二個だろうと、一個だろうと、「&」は「and(AかつB)」、「|」は「or(AまたはB)」を判定する演算子であることに変わりはありません。

では、この二個と一個ではどのような違いがあるのでしょうか?

それは条件式を判定する評価方法です。

条件式の判定順序

「()」なしの同列による条件式の場合、条件式は左辺⇒右辺の順に判定されます。

これは下記のようなコードを実行してみるとよくわかります。

Console.WriteLine(FalseFunc() & TrueFunc());
​
bool FalseFunc()
{
    Console.WriteLine("falseを返すよ!");
    return false;
}
​
bool TrueFunc()
{
    Console.WriteLine("trueを返すよ!");
    return true;
}
コンソール画面

falseを返すよ!
trueを返すよ!
False

左の条件(のメソッド)、右の条件(のメソッド)の順番に実行されていますね。

ここで、条件式をよく見てみましょう。

判定で扱っている論理演算子は「&」なので、左辺と右辺の条件式が両方ともtrueの時にしかtrueになりません。つまり、左辺がfalseの時点で演算結果もfalseとなることが確定している訳です。

これは「|」演算子でも同様のことが起きえます。

// ↓左辺がtrueの時点でtrue確定
Console.WriteLine(TrueFunc() | FalseFunc());
​
bool FalseFunc()
{
    Console.WriteLine("falseを返すよ!");
    return false;
}
​
bool TrueFunc()
{
    Console.WriteLine("trueを返すよ!");
    return true;
}
コンソール画面

trueを返すよ!
falseを返すよ!
True

こちらは左辺がtrueの時点で演算結果もtrueとなることが確定しています。

であれば、右辺の条件式を判定する意味ってないですよね。

そこで多くのプログラミング言語では、左辺の評価時点で結果が確定したら右辺は評価しないという評価方法が提供されています。

これが記号二個続きで表される評価方法「短絡評価」です。

短絡評価とは?

短絡評価は、先程もお伝えした通り、左辺の評価時点で結果が確定したら右辺は評価しないという評価方法です。ショートサーキットと呼ばれることもあります。

// ↓「&」が二個なので、左辺がfalseだったら右辺は判定しない!
Console.WriteLine(FalseFunc() && TrueFunc());
​
bool FalseFunc()
{
    Console.WriteLine("falseを返すよ!");
    return false;
}
​
bool TrueFunc()
{
    Console.WriteLine("trueを返すよ!");
    return true;
}
コンソール画面

falseを返すよ!
False

左辺の時点で結果が確定したため、右辺の「TrueFunc」メソッドが実行されなかったのが、コンソール画面への出力メッセージからわかりますね。

完全評価とは?

反対に、左辺の時点で結果が確定していようと必ず右辺も判定する評価方法のことを「完全評価」といいます

これが「&」や「|」の記号ひとつで表される評価方法です。

// ↓左辺の時点で結果は確定しているけど、右辺も評価する!
Console.WriteLine(FalseFunc() & TrueFunc());
​
bool FalseFunc()
{
    Console.WriteLine("falseを返すよ!");
    return false;
}
​
bool TrueFunc()
{
    Console.WriteLine("trueを返すよ!");
    return true;
}
コンソール画面

falseを返すよ!
trueを返すよ!
False

左辺の時点で結果は確定しているが、右辺の「TrueFunc」メソッドも実行されていることが、コンソール画面への出力メッセージからわかりますね。

どう使い分けるべき?

短絡評価の場合、右辺処理が必ず実行されることを前提としていると問題が発生するというデメリットがあります。が、そもそもそのようなコーディングはバグの元なので望ましくないです。

必ず実行したい処理があるなら条件判定の前に実行しておく。その上で条件判定を行うようにしてあげた方が可読性も保守性もあがります。

そのため、原則使い分ける必要はありません。
無駄な処理は行わない短絡評価一択でOK

ただ、少し役割は変化しますが、ビット演算と呼ばれるもので記号ひとつバージョンの「|」演算子は使う機会があります。列挙型との組み合わせで使うことが多いのですが、こちらはきちんと説明しようとすると長くなる+色々ややこしいので割愛。

まとめ

  • 条件式は基本的に左辺⇒右辺の順に判定される。そのため、左辺の評価時点で結果が確定することがある
  • 完全評価が左辺・右辺のどちらも必ず評価するのに対して、短絡評価は左辺の評価時点で結果が確定した場合は右辺の評価を行わないという特徴がある
  • 完全評価は記号一個で表す(例「&」)のに対して、短絡評価は記号二個で表す(例「||」)
  • 完全評価はバグのリスクがあるため、短絡評価一択でOK

基礎編では短絡評価しか紹介していません。それは上述の通り、原則短絡評価一択でOKだからです。ただ、自分がどういう性質の評価方法を利用しているのか、ということを理解しておくことには意味があると思うので、ここでご紹介させていただきました。

それでは、今回もお疲れ様でした!

また次の記事でお会いしましょう!

Posted by yuumekou