从no-op中删除一个修改过的对象?

Eth*_*han 4 ruby set

请参阅下面的示例

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]]预期的.)这是一个错误吗?

mu *_*ort 5

是的,这是一个错误或至少我称之为一个错误.有些人会称之为"一个意外泄漏到外部世界的实施细节",但这只是花哨的裤子城市男孩谈论bug.

问题有两个主要原因:

  1. 您正在修改Set的元素而不知道它.
  2. 标准Ruby 实现为哈希.

结果是你正在修改内部Hash的密钥,而Hash不知道它,并且将可怜的Hash混淆到不知道它有什么密钥.Hash类有一个rehash方法:

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
Run Code Online (Sandbox Code Playgroud)

请注意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?)将正常工作.