Menu
Coddy logo textTech

המודול 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 אונליין