请参阅下面的示例
require "set"
s = [[1, 2], [3, 4]].to_set # s = {[1, 2], [3, 4]}
m = s.max_by {|a| a[0]} # m = [3, 4]
m[0] = 9 # m = [9, 4], s = {[1, 2], [9, 4]}
s.delete(m) # s = {[1, 2], [9, 4]} ?????
Run Code Online (Sandbox Code Playgroud)
这与阵列的行为不同.(如果我们删除.to_set,我们会得到s = [[1, 2]]预期的.)这是一个错误吗?
是的,这是一个错误或至少我称之为一个错误.有些人会称之为"一个意外泄漏到外部世界的实施细节",但这只是花哨的裤子城市男孩谈论bug.
问题有两个主要原因:
结果是你正在修改内部Hash的密钥,而Hash不知道它,并且将可怜的Hash混淆到不知道它有什么密钥.Hash类有一个rehash方法:
rehash→hsh
根据每个键的当前哈希值重建哈希值.如果键对象的值自插入后已更改,则此方法将重新索引hsh.
Run Code Online (Sandbox Code Playgroud)a = [ "a", "b" ] c = [ "c", "d" ] h = { a => 100, c => 300 } h[a] #=> 100 a[0] = "z" h[a] #=> nil h.rehash #=> {["z", "b"]=>100, ["c", "d"]=>300} h[a] #=> 100
请注意rehash文档中包含的示例中的有趣行为.哈希使用k.hash键的值来跟踪事物k.如果您将数组作为键并更改数组,则也可以更改数组的hash值; 结果是Hash仍然将该数组作为键,但Hash将无法将该数组作为键来查找,因为它将在存储桶中查找新hash值,但该数组将位于存储桶中旧的hash价值.但是,如果你rehash是Hash,它会突然能够再次找到它的所有键并且衰老消失了.非数组键也会出现类似的问题:你只需要更改键,使其hash值发生变化,包含该键的Hash会混淆并在你迷失之前四处游荡rehash.
该集合类使用哈希内部存储其成员和成员作为哈希的键.因此,如果您更改成员,该集将会混淆.如果Set有一个rehash方法,那么你可以通过将头部设置在头部rehash以便将某些感觉敲入其中来解决问题.唉,Set中没有这样的方法.但是,您可以自己修补:
class Set
def rehash
@hash.rehash
end
end
Run Code Online (Sandbox Code Playgroud)
然后你可以更改键,调用rehashSet,你的delete(和其他各种方法member?)将正常工作.