オブジェクトの順序
自作クラスのオブジェクトを Array に詰めて sort してみる。
class OreOre def initialize(val) @value = val end attr_accessor :value def inspect; "o:#{@value}" end end ary = 5.downto(1).inject([]){|a, i| a << OreOre.new(i)} # => [o:5, o:4, o:3, o:2, o:1] ary.sort # ~> -:8:in `sort': undefined method `<=>' for 5:OreOre (NoMethodError)
オブジェクトの大小関係を調べるためには UFO 演算子を使うらしい。
class OreOre def <=>(o) @value - o.value # テキトーだなぁ rescue nil end end ary.sort # => [o:1, o:2, o:3, o:4, o:5]
できた。
UFO 演算子は左辺と右辺を比較して結果を数値で返す。
左辺 <=> 右辺の値は、以下の値を返す約束になっている。
左辺 <=> 右辺 | 値 |
---|---|
左辺 < 右辺 | -1 (負数) |
左辺 == 右辺 | 0 |
左辺 > 右辺 | 1 (正数) |
比較不能 | nil |
ところが、このままだとふつうの比較ができない。
o0 = OreOre.new(0) o1 = OreOre.new(1) o0 < o1 # ~> -:21:in `<top (required)>': undefined method `<' for 0:OreOre (NoMethodError) o0 > o1 # ~> -:22:in `<top (required)>': undefined method `>' for 0:OreOre (NoMethodError)
<, <=, >=, >
をそれぞれ定義しても良いが、<=>
があれば Comparable が自動生成してくれる。
class OreOre include Comparable end o0 = OreOre.new(0) o1 = OreOre.new(1) o0 < o1 # => true o0 > o1 # => false
ここまでくると、次に気になるのは Range の端点になれるか?
o2 = OreOre.new(2) o3 = OreOre.new(3) r = o0..o2 # => o:0..o:2 r.cover?(o1) # => true r.cover?(o3) # => false
できてるかと思いきや、
r.include?(o1) # ~> -:34:in `each': can't iterate from OreOre (TypeError) r === o1 # ~> -:35:in `each': can't iterate from OreOre (TypeError) r.each{|o| puts o.value} # ~> -:36:in `each': can't iterate from OreOre (TypeError)
やっぱりダメ。
たぶん each するには succ がいるんだよね。
class OreOre def succ OreOre.new(@value+1) # テキトーすぐる end end r.include?(o1) # => true r === o1 # => true r === o3 # => false r.each{|o| puts o.value} # >> 0 # >> 1 # >> 2
できた。
けど、ふつうは、こんな簡単に succ 実装できないよな。