Comparableモジュールって何?
Comparableモジュールをincludeすると、< <= == > >= between?
がそのクラスでも使えるようになります。
Integer
やString
はこのComparableをincludeしているので比較演算ができるということです。
Integer.include?(Comparable) #=> true String.include?(Comparable) #=> true
数値でも文字列でもなくて比較したくなるのってどんな時かというと、役職、ランク、漢数字などを実装したいときとか。
今回は和風なランク分けに使う松竹梅をモジュールとして実装してみたいと思います。
松竹梅で考えるComparable
Comparableを使えるようにする
Comparableモジュールを使えるようにするためには、<=>
演算子(宇宙船演算子)を実装する必要があります。
<=>
演算子が実装されているとsort
が使えるようになるというのも大きなメリットです。
p [2,8,7,3,6].sort #=> [2, 3, 6, 7, 8]
a <=> bが次の結果を返すように実装する必要があります。
- aがbより大きいなら正の整数
- aとbが等しいなら0
- aがbより小さいなら負の整数
- aとbが比較できない場合はnil
コード化する
module Shochikubai include Comparable def digitizing_rank(rank) case rank when '松' then 3 when '竹' then 2 when '梅' then 1 else 0 end end def <=>(other) if other.is_a?(Shochikubai) digitizing_rank(rank) <=> digitizing_rank(other.rank) else nil end end end
インクルード先でattr_accessor :rank
してもらうのを前提にしています。
<=>
メソッド定義の中で<=>
を使っているのはややこしいですがdigitizing_rank
の結果必ず数値が返ってくるので、Integerクラスの<=>
を呼んでいることになります。
integerクラスの<=>
を利用すれば、松と梅なら松の方が大きいみたいな処理を実装する必要はありません。とても便利です。
次に、実際にShochikubaiモジュールをincludeしてみます。
includeする
class Unaju include Shochikubai attr_accessor :rank def initialize(rank) @rank = rank end def return_unagi_num case rank when '松' then 6 when '竹' then 4 when '梅' then 2 else 1 end end end
うな重クラスを実装してShochikubaiをインクルードしました。
うな重クラスにはランクに応じてうなぎの数を返すreturn_unagi_numメソッドを実装しました。
実際に動かしてみます。
うな重を比較する
rich_unaju = Unaju.new('松') middle_unaju = Unaju.new('竹') cheap_unaju = Unaju.new("梅") unatama = Unaju.new('うな玉丼') p rich_unaju > cheap_unaju #=>true p rich_unaju == cheap_unaju #=> false p rich_unaju < cheap_unaju #=>false
うな重をソートする
ranking = [middle_unaju,rich_unaju,unatama,cheap_unaju].sort.reverse ranking.each do|unaju| puts "#{unaju.rank}にはうなぎが#{unaju.return_unagi_num}切れ載っている" end #=>松にはうなぎが6切れ載っている # 竹にはうなぎが4切れ載っている # 梅にはうなぎが2切れ載っている # うな玉丼にはうなぎが1切れ載っている
無事うな重をランクごとに比較、ソートできました!
以上です
同じ要領でやれば"警視総監から巡査までを序列順でソート"や"SABCでランク付け"みたいなことが簡単にできますね。
うなぎ食べたいなー。