ハッシュ内のオブジェクトが同一でないことを保証する方法
概要
Ruby のドキュメントには、Book クラスに 3 番目の属性を追加して以下に変更したハッシュの使用方法の例と、以下の 3 番目の書籍レビューが含まれています。元のコードはここで見ることができます。
元のコードでクラスに 2 つの属性がある場合、ブック クラスのハッシュ メソッドは 2 つのインスタンス値の間に ^ を使用しました。ハッシュ メソッドを変更せずにクラスに 3 番目の属性を追加しました。この非常に限定されたデータ セットでは、同一のオブジェクトをハッシュに追加しないという点でコードは引き続き機能します。
そこで私の質問は、オブジェクトに 3 番目の属性がある場合、同一のオブジェクトがハッシュに追加されないことを保証するために、ハッシュ メソッドを変更する必要があるかということです。もしそうなら、どのようにして? (注: ^ について読んでも、このハッシュ メソッドでどのように機能するのかはよくわかりません)
class Book
attr_reader :author, :title, :year
def initialize(author, title, year)
@author = author
@title = title
@year = year
end
def ==(other)
self.class === other &&
other.author == @author &&
other.title == @title &&
other.year == @year
end
alias eql? ==
def hash
@author.hash ^ @title.hash # XOR
end
end
book1 = Book.new 'matz', 'Ruby in a Nutshell', 1987
book2 = Book.new 'matz', 'Ruby in a Nutshell', 1987
book3 = Book.new 'matz', 'Ruby in a Nutshell', 2015 # added by me
reviews = {
book1 => 'Great reference!',
book2 => 'Nice and compact!',
book3 => 'Holy Moly, my additional review',
}
puts reviews.length #=> 2
解決策
まさに、たとえば次のようにハッシュ メソッドに年も追加する必要があります。
def hash
@author.hash ^ @title.hash ^ @year.hash
end
または、Array#hash を使用して次のようにします。
def hash
[@author, @title, @year].hash
end
クラスのハッシュをどのように計算するかはそれほど重要ではないことに注意してください。同じ入力に対して常に同じハッシュを計算することが重要です。また、ハッシュ衝突の可能性が非常に低いことを確認する必要があります。