"bar"}] 两个元素彼此相等: arr[0] == arr[1] => tr-6ren">
gpt4 book ai didi

arrays - Ruby 1.8.6 Array#uniq 不删除重复的哈希

转载 作者:数据小太阳 更新时间:2023-10-29 08:57:32 26 4
gpt4 key购买 nike

我在 ruby​​ 1.8.6 控制台中有这个数组:

arr = [{:foo => "bar"}, {:foo => "bar"}]

两个元素彼此相等:

arr[0] == arr[1]
=> true
#just in case there's some "==" vs "===" oddness...
arr[0] === arr[1]
=> true

但是,arr.uniq 不会删除重复项:

arr.uniq
=> [{:foo=>"bar"}, {:foo=>"bar"}]

谁能告诉我这是怎么回事?

编辑:我可以写一个不太聪明的 uniqifier,它使用 include? 如下:

uniqed = []
arr.each do |hash|
unless uniqed.include?(hash)
uniqed << hash
end
end;false
uniqed
=> [{:foo=>"bar"}]

这产生了正确的结果,这使得 uniq 的失败更加神秘。

编辑 2:关于正在发生的事情的一些注释,可能只是为了我自己的清楚。正如 @Ajedi32 在评论中指出的那样,统一化失败是因为这两个元素是不同的对象。一些类定义了 eql?hash 方法,用于比较,意思是“这些实际上是同一件事,即使它们在内存中不是同一个对象”。例如,String 就是这样做的,这就是为什么您可以将两个变量定义为“foo”并且据说它们彼此相等,即使它们不是同一个对象。

Hash 类不会在 Ruby 1.8.6 中执行此操作,所以当 .eql?.hash 被调用时在散列对象上(.hash 方法与散列数据类型无关——它就像散列的校验和类型)它回退到使用 Object 基类中定义的方法,它只是说“它是同一个对象吗在内存中”。

===== 运算符,对于散列对象,已经做了我想要的,即如果两个散列的内容是相同的。我已经重写了 Hash#eql? 来使用它们,如下所示:

class Hash
def eql?(other_hash)
self == other_hash
end
end

但是,我不确定如何处理 Hash#hash:也就是说,我不知道如何为内容相同的两个散列生成相同的校验和并且对于具有不同内容的两个哈希总是不同的。

@Ajedi32 建议我在此处查看 Rubinius 对 Hash#hash 方法的实现 https://github.com/rubinius/rubinius/blob/master/core/hash.rb#L589 ,而我的 Rubinius 实现版本如下所示:

class Hash
def hash
result = self.size
self.each do |key,value|
result ^= key.hash
result ^= value.hash
end
return result
end
end

这似乎确实有效,虽然我不知道“^=”运算符的作用,这让我有点紧张。此外,它非常慢——根据一些原始基准测试,速度大约是原来的 50 倍。这可能会使它使用起来太慢。

编辑 3:一些研究表明“^”是按位异或运算符。当我们有两个输入时,如果输入不同,则 XOR 返回 1(即它为 0,0 和 1,1 返回 0,为 0,1 和 1,0 返回 1)。

所以,一开始我以为是那个意思

result ^= key.hash 

的简写
result = result ^ key.hash

换句话说,在result的当前值和其他东西之间进行XOR,然后将其保存在result中。不过,我仍然不太明白其中的逻辑。我认为 ^ 运算符可能与指针有关,因为在变量上调用它有效,而在变量值上调用它不起作用:例如

var = 1
=> 1
var ^= :foo
=> 14904
1 ^= :foo
SyntaxError: compile error
(irb):11: syntax error, unexpected tOP_ASGN, expecting $end

因此,可以在变量上调用 ^= 而不是变量的值,这让我认为这与引用/取消引用有关。

后来的 Ruby 实现也有 Hash#hash 方法的 C 代码,Rubinius 的实现似乎太慢了。有点卡住了……

最佳答案

出于效率原因,Array#uniq 不使用 == 甚至 === 比较值。根据the docs :

It compares values using their hash and eql? methods for efficiency.

(请注意,我在这里链接了 2.4.2 的文档。虽然 1.8.6 的文档不包含此声明,但我相信它仍然适用于该版本的 Ruby。)

在 Ruby 1.8.6 中,neither Hash#hash nor Hash#eql? are implemented , 所以他们回退到使用 Object#hashObject#eql? :

Equality—At the Object level, == returns true only if obj and other are the same object. Typically, this method is overridden in descendent classes to provide class-specific meaning.

[...]

The eql? method returns true if obj and anObject have the same value. Used by Hash to test members for equality. For objects of class Object, eql? is synonymous with ==.

所以根据 Array#uniq,这两个哈希值是不同的对象,因此是唯一的。

要解决此问题,您可以尝试定义 Hash#hashHash#eql?你自己。如何做到这一点的细节留给读者作为练习。不过,您可能会发现引用 Rubinius's implementation of these methods 很有帮助。 .

关于arrays - Ruby 1.8.6 Array#uniq 不删除重复的哈希,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47290871/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com