Rubyリファレンスマニュアルには、『比較演算子を許すクラスのMix-in』とある。

C++のような、レガシーなオブジェクト指向プログラミング言語においては、クラスは、『ユーザー定義型』とも呼ばれている。型なのであるから、数値型や文字型のように、算術演算子や比較演算子が使えると嬉しいし(別に無理して使う必要はないけれど)、ちょっとカッコいい。『俺は型を定義したんだぞ。その証拠に演算子が利用できるんだ』と、少し誇らしげな気持ちになれる。

しかし、ここで考えていただきたいのが、ユーザーが勝手に定義したクラスに『演算子を作用させる』とは、はていかなることかということなのである。

#犬クラス
class Dog
    #様々なメンバー
end
pochi = Dog.new
pochi += 1

上記コードの変さ加減は説明するまでもあるまい。『はて?犬に1を加算するとはこれいかに?』という世界なのだ。

では、このコードはどうだろうか。

pochi = Dog.new
tarou = Dog.new
if pochi>tarou ; puts "ポチはタロウより大きい" end

これだとなんとなく意味が通りそうだ。おそらく犬の体長か体高を比較したいのだろうと思われる。

しかし、人間が見て意味あることであっても、Rubyインタプリタからすると、全く知ったことではないのである。

では、pochi>tarou という比較演算を有効にしたい場合はどうすればよいのかというと、クラスのインスタンスに演算子を作用させたときの振る舞いをいちいち記述しておく必要があるのだ。

これを『演算子のオーバーロード』と呼ぶ。

実はこれをやりだすとキリがなく、実用に耐えうる比較を行いたい場合、>(大なり)演算子の振る舞いだけを定義すれば済む問題ではなく、次の演算子を全て定義しておく必要があるのだ。

>
<
>=
<=
==

『これを全部定義するのか?本気で言っているのか貴様?』と文句のひとつもつけたくなるわけだが、C++を筆頭とするレガシーオブジェクト指向プログラミング言語は、実際本気で言っているので始末に困るのである。

しかし我らがRubyは一味違う。Ruby特有の比較演算子<=>を定義しておけば、それを活用して前出の比較演算子を使用可能にしてくれる、ありがたいモジュールがあるのだ。

それが、Comparable なのである。

実際にコードで説明すると理解しやすいだろう。

001 | #犬クラスの定義
002 | class Dog
003 |     include Comparable #ComparableのMix-in
004 |     attr_reader:height
005 |     #初期化処理
006 |     #体高を受け取ってメンバーに格納する。
007 |     def initialize(h)
008 |         @height=h
009 |     end
010 |     #比較演算子のオーバーロード
011 |     def <=>(other)
012 |         return @height == other.height ? 0 :
013 |              @height > other.height ? 1 : -1
014 |     end
015 | end
016 | 
017 | pochi=Dog.new(50)
018 | tarou=Dog.new(45)
019 | 
020 | p pochi > tarou

このコードのキモは、3行目のComparableのインクルード、そして、11行目からの<=>演算子のオーバーロードだ。

実際に、色々な体高でポチとタロウを生成してみて、比較演算子も書き換えて実行してみていただきたい。

また、3行目をコメントにすると、20行目の比較演算がエラーになってしまうというのも確認しておいていただきたいものだ。


最終更新:2009年03月10日 22:37