Menu
Coddy logo textTech

שוויון ו-hash

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

ל-Ruby יש כמה סוגים של שוויון. == בודק שוויון ערכים, שאותו מגדירים עבור המחלקה שלך. גיבובים ו-uniq משתמשים בשתי שיטות אחרות במקום זאת: eql? ו-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 אונליין