Menu
CoddyTech

שוויון ו-hash

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

ל־Ruby יש כמה סוגים של שוויון. == הוא שוויון לפי ערך, שאותו מגדירים עבור המחלקה שלך. Hashes ו־uniq משתמשים בשתי מתודות אחרות במקום זאת: eql? ו־hash. כשמגדירים רק את ==, נקודות שוות עדיין נחשבות למפתחות שונים ב־hash:

class Point
  attr_reader :x, :y

  def initialize(x, y)
    @x = x
    @y = y
  end

  def ==(other)
    other.is_a?(Point) && x == other.x && y == other.y
  end
end

a = Point.new(1, 2)
b = Point.new(1, 2)
puts a == b
puts [a, b].uniq.size
puts({ a => "first" }[b].inspect)

פלט:

true
2
nil

אובייקט Hash משווה תחילה ערכי hash, מספרים המחושבים מאובייקט, ואז בודק את המועמדים באמצעות eql?. הגדירו את שניהם על סמך אותם שדות כמו ==. alias eql? == עושה שימוש חוזר ב-==, ו-[x, y].hash משלב את השדות:

class Point
  attr_reader :x, :y

  def initialize(x, y)
    @x = x
    @y = y
  end

  def ==(other)
    other.is_a?(Point) && x == other.x && y == other.y
  end
  alias eql? ==

  def hash
    [x, y].hash
  end
end

a = Point.new(1, 2)
b = Point.new(1, 2)
puts [a, b, Point.new(3, 4)].uniq.size
puts({ a => "first" }[b])

פלט:

2
first

הכלל הוא: לאובייקטים שעבורם eql? מחזירים true חייב להיות אותו hash. לשני אובייקטים שונים עשוי להיות אותו ערך hash במקרה, ולכן eql? עדיין בודקת. כך ספירה באמצעות Hash פועלת לפי ערכים במקום לפי זהויות:

class Word
  attr_reader :text

  def initialize(text)
    @text = text.downcase
  end

  def ==(other)
    other.is_a?(Word) && text == other.text
  end
  alias eql? ==

  def hash
    text.hash
  end
end

counts = Hash.new(0)
%w[Ruby ruby RUBY gem].each { |t| counts[Word.new(t)] += 1 }
counts.each { |word, n| puts "#{word.text}: #{n}" }

פלט:

ruby: 3
gem: 1

equal? מציין זהות ויש להשאירו ללא הגדרה מחדש. ערכים מובנים כבר פועלים לפי הכללים האלה, ולכן מחרוזות ומערכים יכולים לשמש כמפתחות של Hash:

a == b          # אותו ערך (אתם מגדירים אותו)
a.eql?(b)       # אותו ערך עבור מפתחות Hash ו-uniq (כינוי של ==)
a.hash          # מספר; אובייקטים שווים מחזירים מספרים שווים
a.equal?(b)     # אותו אובייקט (לעולם אין להגדיר מחדש)
challenge icon

אתגר

קל

השלימו את המחלקה Point כך שנקודות עם אותן קואורדינטות יהיו שוות באמצעות ==, eql? ו-hash, ויוכלו לשמש כמפתחות ב-Hash. to_s מחזירה (1, 2).

הקוד שסופק קורא נקודות משורות בפורמט x,y. הוא מדפיס את מספר הנקודות הייחודיות באמצעות uniq, לאחר מכן סופר את הביקורים בכל נקודה ב-Hash ומדפיס אותם לפי סדר הביקור הראשון כ-(1, 2): 2, ולבסוף מציין אם Point.new(0, 0) חדשה היא מפתח.

כתבו את הקוד בקובץ point.rb. הקובץ main.rb מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

require_relative 'point'

# קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input = []
while (line = gets)
  input << line.chomp
end
points = input.map { |line| Point.new(*line.split(",").map(&:to_i)) }
puts "distinct: #{points.uniq.size}"
visits = Hash.new(0)
points.each { |pt| visits[pt] += 1 }
visits.each { |pt, n| puts "#{pt}: #{n}" }
puts "origin visited: #{visits.key?(Point.new(0, 0))}"
quiz iconבחנו את עצמכם

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

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

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