rubyのUFO演算子とComparableモジュール

2022.07.18

Comparableモジュールとは

比較演算を許すクラスのための Mix-in です。このモジュールをインクルードするクラスは、基本的な比較演算子である <=> 演算子を定義している必要があります。

Ruby 3.1 リファレンスマニュアル

組み込みモジュールなのでincludeする必要があり、<=>が定義されている必要がある。

self <=> other は以下の処理になることが期待されている。(<=>メソッドの中身)

  • self が other より大きいなら正の整数(1)
  • self と other が等しいなら 0
  • self が other より小さいなら負の整数(-1)
  • self と other が比較できない場合は nil

between?(min, max) -> boolclamp(min, max) -> objectのヘルパーメソッドも使用できるようになる。

特徴

年齢の比較したり、配列の中で最大値を出すとき。

Comparableモジュール使わない場合。カラムの指定が必要になる。

class Person
  attr_accessor :age

  def initialize(value)
    @age = value
  end
end

a = Person.new(1)
b = Person.new(2)
c = Person.new(3)
d = Person.new(5)
list = [d,c,a,b]

p a.age < b.age #=> true
p list.max #=> `max': comparison of Person with Person failed (ArgumentError)
p a < b #=> undefined method `<' for #<Person:0x00007f7a1289af38 @age=1> (NoMethodError)

UFO演算子のみの場合。maxやmin,sort等では使えるがオブジェクトでは使えない。

class Person
  attr_accessor :age

  def initialize(value)
    @age = value
  end

  def <=>(other)
    @age <=> other.age
  end
end

a = Person.new(1)
b = Person.new(2)
c = Person.new(3)
d = Person.new(5)
list = [d,c,a,b]

p a.age < b.age #=> true
p list.max #=> #<Person:0x00007f7a9684ecd0 @age=5>
p a < b #=> undefined method `<' for #<Person:0x00007fb8328f6e60 @age=1> (NoMethodError)

Comparableモジュール使う場合。オブジェクトで比較演算できたり、ヘルパーメソッドが使えるようになる。

class Person
  include Comparable
  attr_accessor :age

  def initialize(value)
    @age = value
  end

  def <=>(other)
    @age <=> other.age
  end

end

a = Person.new(1)
b = Person.new(2)
c = Person.new(3)
d = Person.new(5)
list = [d,c,a,b]

p a.age < b.age #=> true
p list.max #=> #<Person:0x00007f7a9684ecd0 @age=5>
p a < b #=> true

#以下も可能
p b <= c #=> true
p c == a #=> false
p a > b #=> false
p b >= c #=> false
p b.between?(a, c) #=> true
p d.clamp(a, c) #=> #<Person:0x00007f8d2d8ceaf8 @age=3>
p d.clamp(a, b) #=> #<Person:0x00007f8d2d8ceb70 @age=2>

まとめ

  • ufo演算子を4つのルールを守り正しく定義する。何をもって大小等しいを決めるのは自分で決めることができる。
  • ufo演算子の定義のみだとsortやmax,minに影響がある。オブジェクトで比較演算はできない。
  • ufo演算子が定義されたクラスにComparableモジュールを追加するとオブジェクトで比較演算ができるようになる。

比較演算に多く利用されるカラムがわかっている場合に定義すると、カラムを指定せずともオブジェクトで計算してくれるので楽になる気がしました。(ん〜具体例が思い浮かばない。)