Ruby の論理演算子、&& と || は制御構造で & と | はメソッド

Ruby で AND, OR の論理演算を行うための論理演算子には {&&,||} と {&,|} の 2 種類あります。この 2 種類は似ていてまぎらわしいのですが、

{&&,||} は制御構造で、{&,|} はメソッド

とだけ覚えておきえば区別できます。

これだけ覚えておけば、

  • (1) {&,|} の両辺は必ず実行される。{&&,||} の右辺は実行されないことがある
  • (2) {&&,||} の両辺は任意のオブジェクト、{&,|} の左辺は true から false のみ

という違いは簡単に理解できます。

(1) {&,|} の両辺は必ず実行される。{&&,||} の右辺は実行されないことがある

{&, |} はメソッドでした。ですので、

foo & bar
foo | bar

は、

foo.&(bar)
foo.|(bar)

というメソッドで書き直すことができます。こう書き直してみると、foo も bar も実行されることがわかります。一方、{&&,||} は

foo.&&(bar)
foo.||(bar)

というメソッドではなく制御構造なので、判定結果が明かになった時点で実行を中止することができます。ですので、左辺だけで判定できる場合は右辺は実行されません。

(2) {&&,||} の両辺は任意のオブジェクト、{&,|} の左辺は true から false のみ

Ruby の条件文では nil と false 以外のものは全て true と評価されます。すなわち、Ruby ではどんなオブジェクトでも必ず true か false に評価できます。これがあるので、{&&,||} を実行するときには、両辺を true か false に評価してから論理演算することができます。ですので、{&&,||} の両辺はどんなオブジェクトでも ok です。

一方、{&,|} はメソッドですので、左辺のオブジェクトに {&,|} メソッドがないと、

:foo & true #=> NoMethodError: undefined method `&' for :foo:Symbol

と NoMethodError になってしまいます。論理演算をする {&,|} メソッドをもったオブジェクトは、true か false のみですので、{&,|} の左辺は true か false に限定されます。

ここで、

{&,|} の左辺は true から false のみ

というのが言い過ぎであることを白状しなくてはなりません。というのも、{&,|} がメソッドである以上、自分でいかようにも定義することができますし、Array や Fixnum のように組込みクラスで {&,|} メソッドが定義されているものもあります。この場合、{&,|} の左辺に true や false 以外のオブジェクトを置けますが、論理演算以外の演算が実行されます。例で挙げた Array でいうと {&,|} は集合演算になりますし、Fixnum でいうと {&,|} ビット演算になります。

おわりに

{&&,||} と {&,|} の違いを表面的に理解するだけなら、最初に挙げた 「(1) {&,|} の両辺は必ず実行される。{&&,||} の右辺は実行されないことがある」と「演算子の優先順位が違う」だけ押えておけば十分ですが、「片方が制御構造で片方がメソッド」と覚えておいたほうが、全体の理解に役立ちそうなのでエントリにしてみました。人に聞かれたときにも、最初に「片方が制御構造で片方がメソッド」と説明すると後が早いのでそうしています。

あと、同様の制御構造に and, or もあるのですが、使い過ぎると perlish になるので省略。