Menu
CoddyTech

המודול Comparable

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Ruby של Coddy. שיעור 29 מתוך 57.

המודול המובנה Comparable של Ruby מעניק למחלקה את האופרטורים <, <=, ==, >, >= ואת המתודות between? ו־clamp. הוא זקוק למתודה אחת מהמחלקה: <=>, שמחזירה מספר שלילי, 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

למספרים ולמחרוזות כבר יש <=>, ולכן מחלקה בדרך כלל מאצילה את ההשוואה לאחד הערכים שלה. מערכים מושווים איבר אחר איבר, וכך ממיינים לפי כמה שדות בו־זמנית:

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
true

sort, min ו-max משתמשות גם הן ב-<=>, ולכן אפשר למיין אובייקטים מהמחלקה בלי בלוק. clamp משאירה ערך בין שני ערכים אחרים:

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 מעלה ArgumentError עבור < ומחזירה false עבור ==, במקום להיכשל עם NoMethodError מבלבלת:

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 נכשלה
challenge icon

אתגר

קל

השלימו את המחלקה 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"))}"
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Ruby אונליין