Menu
CoddyTech

المساواة والتجزئة

جزء من قسم البرمجة كائنية التوجه في رحلة 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? القيمة نفسها لـ 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? خاصةً بالهوية، ولا ينبغي أبدًا إعادة تعريفها. تتبع القيم المضمنة هذه القواعد بالفعل، ولهذا السبب تعمل السلاسل والمصفوفات كمفاتيح للتجزئة:

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 عبر الإنترنت