Comparableモジュール
CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 29/57。
Rubyに組み込まれているモジュール Comparable を使うと、クラスに演算子 <、<=、==、>、>= と、メソッド between? および clamp を追加できます。クラスに必要なのは1つのメソッド <=> です。このメソッドは、オブジェクトが小さい場合は負の数、等しい場合は0、大きい場合は正の数を返します。
class Weight
include Comparable
attr_reader :grams
def initialize(grams)
@grams = grams
end
def <=>(other)
grams <=> other.grams
end
end
a = Weight.new(500)
b = Weight.new(1200)
puts a < b
puts a == Weight.new(500)
puts b.between?(a, Weight.new(2000))出力:
true
true
true数値と文字列にはすでに <=> があるため、クラスでは通常、値の1つに処理を委譲します。配列は要素ごとに比較されるので、複数のフィールドを一度に使って並べ替えられます。
class Person
include Comparable
attr_reader :last, :first
def initialize(first, last)
@first = first
@last = last
end
def <=>(other)
[last, first] <=> [other.last, other.first]
end
end
a = Person.new("Ada", "Lovelace")
b = Person.new("Alan", "Turing")
c = Person.new("Byron", "Lovelace")
puts a < c
puts b > c出力:
true
truesort、min、maxも<=>を使うため、このクラスのオブジェクトはブロックなしでソートできます。clampは値を2つの値の間に収めます。
class Weight
include Comparable
attr_reader :grams
def initialize(grams)
@grams = grams
end
def <=>(other)
grams <=> other.grams
end
def to_s
"#{grams}g"
end
end
list = [Weight.new(900), Weight.new(150), Weight.new(400)]
puts list.sort.join(", ")
puts list.max
puts Weight.new(5000).clamp(Weight.new(0), Weight.new(1000))出力:
150g, 400g, 900g
900g
1000g比較対象のオブジェクトを比較できない場合、<=> は nil を返すべきです。すると Comparable は、紛らわしい NoMethodError で失敗する代わりに、< に対して ArgumentError を発生させ、== に対して false を返します。
def <=>(other)
return nil unless other.is_a?(Weight)
grams <=> other.grams
end
Weight.new(1) == 5 # false
Weight.new(1) < 5 # ArgumentError: Weight と 5 の比較に失敗しましたチャレンジ
簡単Weightクラスを完成させてください。Weight.parse(text)は500g、1.2kg、または2lb(1 lb は453.6 g)を読み取り、グラム数を保存します。Comparableをインクルードし、グラム数に対する<=>を定義してください。to_sは、1200gのようにグラム数を整数に丸めて返します。
提供されたコードは、各入力行を解析してから、重さをソートした結果、最も重いもの、最も軽いもの、最初の重さが1 kgから2 kgの間にあるかどうか、および最大1 kgに制限した最初の重さを出力します。
コードはweight.rbに記述してください。main.rbには提供された入出力コードがあり、編集できません。
自分で試してみよう
require_relative 'weight'
# 提供された入出力コード: そのままにしてください
input = []
while (line = gets)
input << line.chomp
end
weights = input.map { |line| Weight.parse(line) }
puts "sorted: #{weights.sort.join(", ")}"
puts "heaviest: #{weights.max}"
puts "lightest: #{weights.min}"
puts "first between 1kg and 2kg: #{weights[0].between?(Weight.parse("1kg"), Weight.parse("2kg"))}"
puts "first clamped: #{weights[0].clamp(Weight.parse("0g"), Weight.parse("1kg"))}"
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rubyオンラインコンパイラ