Code Fix

中級

Comparable を include しても <=> が無いと比較演算子は使えない

Comparableモジュールをincludeするだけでは <、>、between? などのメソッドは動きません。これらのメソッドは内部で<=>(UFO演算子)を呼び出す仕組みなので、クラス自身に<=>を定義しておく必要があります。

エラーメッセージの読み方

main.rb:11:in `<': comparison of Money with Money failed (ArgumentError)
main.rb
ファイル名
11
行番号 — 実際にクラッシュした行
<
発生場所 — <main> ならトップレベル、メソッド名ならそのメソッドの中
ArgumentError
例外クラス — 何が起きたか。ここを検索するのが最短です
comparison of Money with Money failed
詳細メッセージ — どの値が問題だったか

このエラーが出る典型パターン

パターン1

 1  class Money
 2    include Comparable
 3    attr_reader :amount
 4    def initialize(amount)
 5      @amount = amount
 6    end
 7  
 8                                                    
                             ^
 9  end
10  
11  puts Money.new(100) < Money.new(200)
main.rb:11:in `<': comparison of Money with Money failed (ArgumentError) from main.rb:11:in `<main>'

Comparableはincludeするだけでは動きません。比較の基準となる<=>を自分で定義する必要があります。

直し方: (空) def <=>(other) amount <=> other.amount end にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  class Weight
 2    include Comparable
 3    attr_reader :grams
 4    def initialize(grams)
 5      @grams = grams
 6    end
 7  
 8                                                  
                            ^
 9  end
10  
11  puts Weight.new(100) < Weight.new(200)
main.rb:11:in `<': comparison of Weight with Weight failed (ArgumentError) from main.rb:11:in `<main>'

<、>、between?などのメソッドはすべて内部で<=>を呼び出す仕組みです。<=>が無ければどれも動きません。

直し方: (空) def <=>(other) grams <=> other.grams end にします。

この問題を解いてみる →

パターン3

 1  class Score
 2    include Comparable
 3    attr_reader :points
 4    def initialize(points)
 5      @points = points
 6    end
 7  
 8                                                    
                             ^
 9  end
10  
11  puts Score.new(10) < Score.new(20)
main.rb:11:in `<': comparison of Score with Score failed (ArgumentError) from main.rb:11:in `<main>'

「includeした時点で比較演算子が使えるようになるはず」という思い込みは誤りです。<=>の実装は自分の責任です。

直し方: (空) def <=>(other) points <=> other.points end にします。

この問題を解いてみる →

パターン4

 1  class Distance
 2    include Comparable
 3    attr_reader :meters
 4    def initialize(meters)
 5      @meters = meters
 6    end
 7  
 8                                                    
                             ^
 9  end
10  
11  puts Distance.new(5) < Distance.new(10)
main.rb:11:in `<': comparison of Distance with Distance failed (ArgumentError) from main.rb:11:in `<main>'

<=>は「自分がotherより小さいか同じか大きいか」を-1・0・1で返すメソッドです。これがComparableの土台になります。

直し方: (空) def <=>(other) meters <=> other.meters end にします。

この問題を解いてみる →

パターン5

 1  class Rank
 2    include Comparable
 3    attr_reader :value
 4    def initialize(value)
 5      @value = value
 6    end
 7  
 8                                                  
                            ^
 9  end
10  
11  puts Rank.new(1) < Rank.new(2)
main.rb:11:in `<': comparison of Rank with Rank failed (ArgumentError) from main.rb:11:in `<main>'

<=>を実装すれば、<や>だけでなくbetween?やclampなどComparableの全メソッドが一度に使えるようになります。

直し方: (空) def <=>(other) value <=> other.value end にします。

この問題を解いてみる →

よくある誤解

「Comparableをincludeした時点で比較演算子が自動的に使えるようになるはず」という思い込みは誤りです。Comparableは<=>の結果を使って他の比較メソッドを組み立てる仕組みにすぎず、<=>自体は自分で実装しなければなりません。

まとめ

Comparable を include しても <=> が無いと比較演算子は使えないは中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)