- 作者: Peter J.Jones
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/19
- メディア: Kindle版
- この商品を含むブログ (5件) を見る
項目13 "<=>"とComparableモジュールで比較を実装しよう
例にでてくるのはバージョンの比較を実装する時の話。
例えば、"10.10.3"と"10.9.8"のような比較の場合どうするか?
Object#<=>
2つのオブジェクトが等しいかどうかをテストするだけの汎用メソッド。
class Version attr_reader(:major, :minor, :patch) def initialize(version) @major, @minor, @patch = version.split(".").map(&:to_i) end end >> %w(10.10.3 10.9.8).map { |v| Version.new(v) }.sort ArgumentError (comparison of Version with Version failed) # オブジェクトが等しくなければnilを返す >> v1 = Version.new("10.10.3") >> v2 = Version.new("10.9.8") >> v1 <=> v2 => nil
なので<=>を実装する
class Version attr_reader(:major, :minor, :patch) def initialize(version) @major, @minor, @patch = version.split(".").map(&:to_i) end # 追加 def <=>(other) return nil unless other.is_a?(Version) # 各桁を比較し0でないものがあればその値を返す。全部0なら0を返す。 [ major <=> other.major, minor <=> other.minor, patch <=> other.patch, ].detect { |n| !n.zero? } || 0 end end >> Version.new("10.10.3") <=> "a" => nil >> Version.new("10.10.3") <=> Version.new("10.11.3") => -1 >> Version.new("10.11.3") <=> Version.new("10.10.3") => 1 >> Version.new("10.10.3") <=> Version.new("10.10.3") => 0
ついでに比較演算子
"<", "<=", "==", ">", ">=" はComparableモジュールをincludeすればok。
class Version include Comparable # これ追加 attr_reader(:major, :minor, :patch) def initialize(version) @major, @minor, @patch = version.split(".").map(&:to_i) end def <=>(other) return nil unless other.is_a?(Version) [ major <=> other.major, minor <=> other.minor, patch <=> other.patch, ].detect { |n| !n.zero? } || 0 end end >> v1 = Version.new("10.10.3") >> v2 = Version.new("10.11.3") >> [v1 < v2, v1 <= v2, v1 == v2, v1 >= v2, v1 > v2] => [true, true, false, false, false] >> Version.new("10.10.10").between?(v1, v2) >= true
覚えておくべき事項
- オブジェクトの順序は、"<=>演算子を定義し、Comparableモジュールをインクルードして実装しよう
- "<=>"演算子は、左日演算子が右日演算子と比較できないものならnilを返す。
- クラスのために"<=>"を実装した場合、特にインスタンスをハッシュキーとして使うつもりなら、eql?を"=="の別名にすることを検討しよう。 別名にする場合には、hashメソッドもオーバーライドしなければならない。
最後のやつは、こんな感じに実装すると良い。
class Version alias_method(:eql?, :==) def hash [major, minor, patch].hash end end