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 になるので省略。