Menu
Coddy logo textTech

Igualdad y hash

Parte de la sección Programación orientada a objetos del Journey de Ruby de Coddy. Lección 37 de 57.

Ruby tiene varios tipos de igualdad. == es la igualdad de valores, que defines para tu clase. Los hashes y uniq usan en su lugar otros dos métodos: eql? y hash. Al definir únicamente ==, los puntos iguales siguen siendo claves de hash diferentes:

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)

Salida:

true
2
nil

Un Hash primero compara los valores de hash, números calculados a partir de un objeto, y después comprueba los candidatos con eql?. Define ambos a partir de los mismos campos que ==. alias eql? == reutiliza ==, y [x, y].hash combina los campos:

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])

Salida:

2
first

La regla es: los objetos que son eql? deben tener el mismo hash. Dos objetos diferentes pueden compartir un valor de hash por casualidad, por lo que eql? aún realiza la comprobación. Después, contar con un Hash funciona usando valores en lugar de identidades:

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}" }

Salida:

ruby: 3
gem: 1

equal? mantiene la identidad y nunca debe redefinirse. Los valores integrados ya siguen estas reglas, por eso las cadenas y los arrays funcionan como claves de hash:

a == b          # mismo valor (tú lo defines)
a.eql?(b)       # mismo valor para las claves de Hash y uniq (alias de ==)
a.hash          # un número; los objetos iguales dan números iguales
a.equal?(b)     # el mismo objeto (nunca redefinir)
challenge icon

Desafío

Fácil

Completa la clase Point para que los puntos con las mismas coordenadas sean iguales con ==, eql? y hash, y funcionen como claves de Hash. to_s devuelve (1, 2).

El código proporcionado lee los puntos como líneas x,y. Imprime el número de puntos distintos con uniq, después cuenta las visitas a cada punto en un Hash y las imprime en el orden de la primera visita como (1, 2): 2, y finalmente indica si un nuevo Point.new(0, 0) es una clave.

Tu código debe ir en point.rb. main.rb contiene el código proporcionado de entrada/salida y no se puede editar.

Pruébalo tú mismo

require_relative 'point'

# Código de entrada/salida suministrado: déjalo como está
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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Compilador de Ruby online