Igualdade e hash
Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 37 de 57.
Ruby tem vários tipos de igualdade. == é a igualdade de valores, que você define para sua classe. Hashes e uniq usam dois outros métodos: eql? e hash. Com apenas == definido, pontos iguais ainda são chaves 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)Saída:
true
2
nilUm Hash primeiro compara os valores de hash, números calculados a partir de um objeto, e depois verifica os candidatos com eql?. Defina ambos usando os mesmos campos que ==. alias eql? == reutiliza ==, e [x, y].hash combina os 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])Saída:
2
firstA regra é: objetos que são eql? devem ter o mesmo hash. Dois objetos diferentes podem compartilhar um valor de hash por acaso, e é por isso que eql? ainda verifica. Então, contar com um Hash funciona com base nos valores, em vez das 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}" }Saída:
ruby: 3
gem: 1equal? mantém a identidade e nunca deve ser redefinido. Os valores integrados já seguem essas regras, e é por isso que strings e arrays funcionam como chaves de hash:
a == b # mesmo valor (você define)
a.eql?(b) # mesmo valor para chaves de Hash e uniq (alias de ==)
a.hash # um número; objetos iguais dão números iguais
a.equal?(b) # o mesmo objeto (nunca redefina)Desafio
FácilComplete a classe Point para que pontos com as mesmas coordenadas sejam iguais com ==, eql? e hash, e funcionem como chaves de Hash. to_s retorna (1, 2).
O código fornecido lê pontos como linhas x,y. Ele imprime o número de pontos distintos com uniq, depois conta as visitas por ponto em um Hash e as imprime na ordem da primeira visita como (1, 2): 2 e, por fim, informa se um novo Point.new(0, 0) é uma chave.
Seu código deve estar em point.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
require_relative 'point'
# Código de entrada/saída fornecido: mantenha-o 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))}"
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação orientada a objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesVariáveis de InstânciaMétodos e selfto_s e inspectRevisão - Cartão da Biblioteca4Encapsulamento
Métodos privadosMétodos protegidosSetters privadosProtegendo o estadoRevisão - Máquina de venda automática7Polimorfismo e Duck Typing
PolimorfismoDuck TypingMétodos Abstratoscase e ClassesRevisão - Métodos de Pagamento2Acessores e self
Leitores e GravadoresSetters Personalizadosself nos MétodosIdentidade e IgualdadeRecapitulação - Lista de Contatos5Herança
SubclassesSobrescrita de métodosChamando superis_a? e ancestraisRevisão - Folha de pagamento da equipe8Métodos Especiais
Igualdade e hashMétodos de OperadoresIndexação e AdiçãoColeções PersonalizadasRevisão - Frações11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de Ruby online