是的,这是一个错误,或者至少我称之为错误。有人会称之为“一个意外泄露给外界的实现细节”,但这只是花哨的城市男孩对bug的谈话。
问题有两个主要原因:
- 您在 Set 不知情的情况下修改 Set 的元素。
- 标准的 Ruby Set实现为 Hash。
结果是您在 Hash 不知道的情况下修改了内部 Hash 的键,这使可怜的 Hash 变得不知道它有什么键。Hash 类有一个rehash
方法:
重新散列→hsh
根据每个键的当前哈希值重建哈希。如果键对象的值在插入后发生了变化,则此方法将重新索引hsh。
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
哈希使用key 的值来跟踪事物k
。如果你有一个数组作为键并且你改变了数组,你也可以改变数组的hash
值;结果是哈希仍然将该数组作为键,但哈希将无法找到该数组作为键,因为它将在桶中查找新hash
值,但该数组将在桶中旧hash
值。但是,如果你rehash
是 Hash,它会突然再次找到它的所有密钥,并且衰老消失了。非数组键也会出现类似的问题:您只需更改键,使其hash
值更改并且包含该键的哈希会变得混乱并四处游荡,直到您丢失为止rehash
。
Set 类在内部使用 Hash 来存储其成员,并且成员用作散列的键。因此,如果您更改成员,则 Set 将变得混乱。如果 Set 有一个rehash
方法,那么您可以通过将 Set 倒置头部来解决问题,以使其具有rehash
某种意义;唉,Set中没有这样的方法。但是,您可以修改自己的猴子补丁:
class Set
def rehash
@hash.rehash
end
end
然后您可以更改键,调用rehash
Set,您的delete
(以及其他各种方法,例如member?
)将正常工作。