Ruby #union on Array 反应非常奇怪

Lar*_*ter 2 ruby arrays union

我喜欢使用 Array#union 或 #| 方法返回已删除重复项的对象数组。我在我的班级中有一个自定义实现eql?。如果我这样做的话,就会有一种非常奇怪的行为。最多 8 个元素,联合工作正常,但元素更多时,重复项不会被删除。这实际上是红宝石的错误还是我错过了什么?

class A
  attr_accessor :name

  def initialize(name)
    self.name = name
  end

  def eql?(other)
    other.name.eql?(name)
  end
end

as = names.map { |name| A.new(name) }
bs = names.map { |name| A.new(name) }

as | bs
=>
[#<A:0x00007fe503692388 @name="a">,
 #<A:0x00007fe503692310 @name="b">,
 #<A:0x00007fe5036922e8 @name="c">,
 #<A:0x00007fe5036922c0 @name="d">,
 #<A:0x00007fe503692298 @name="e">,
 #<A:0x00007fe503692270 @name="f">,
 #<A:0x00007fe503692248 @name="g">,
 #<A:0x00007fe503692220 @name="h">,
 #<A:0x00007fe5036921f8 @name="i">,
 #<A:0x00007fe5036921d0 @name="j">,
 #<A:0x00007fe5036921a8 @name="k">,
 #<A:0x00007fe503692180 @name="l">,
 #<A:0x00007fe5035732e0 @name="a">,
 #<A:0x00007fe5035732b8 @name="b">,
 #<A:0x00007fe503573290 @name="c">,
 #<A:0x00007fe503573268 @name="d">,
 #<A:0x00007fe503573240 @name="e">,
 #<A:0x00007fe503573218 @name="f">,
 #<A:0x00007fe5035731f0 @name="g">,
 #<A:0x00007fe5035731c8 @name="h">,
 #<A:0x00007fe5035731a0 @name="i">,
 #<A:0x00007fe503573178 @name="j">,
 #<A:0x00007fe503573150 @name="k">,
 #<A:0x00007fe503573128 @name="l">]

as[0..7] | bs[0..7]
=>
[#<A:0x00007fe503692388 @name="a">,
 #<A:0x00007fe503692310 @name="b">,
 #<A:0x00007fe5036922e8 @name="c">,
 #<A:0x00007fe5036922c0 @name="d">,
 #<A:0x00007fe503692298 @name="e">,
 #<A:0x00007fe503692270 @name="f">,
 #<A:0x00007fe503692248 @name="g">,
 #<A:0x00007fe503692220 @name="h">] 
Run Code Online (Sandbox Code Playgroud)

Tja*_*ark 6

You need to implement both eql? and hash methods with accordance to @engineersmnky's comment regarding Object#eql?, that eql? expects the instance hashes to be the same

class A
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def eql?(other)
    other.name.eql?(@name)
  end

  def hash
    [@name].hash
  end
end

#declare inclusive range from 'a' to 'm'

names = 'a'..'m'

as = names.map { |name| A.new(name) }
bs = names.map { |name| A.new(name) }

as | bs



Run Code Online (Sandbox Code Playgroud)

Outputs:

[#<A:0x0000558a74860020 @name="a">,
 #<A:0x0000558a7485bf98 @name="b">,
 #<A:0x0000558a7485bf70 @name="c">,
 #<A:0x0000558a7485bf48 @name="d">,
 #<A:0x0000558a7485bf20 @name="e">,
 #<A:0x0000558a7485bef8 @name="f">,
 #<A:0x0000558a7485bed0 @name="g">,
 #<A:0x0000558a7485bea8 @name="h">,
 #<A:0x0000558a7485bd90 @name="i">,
 #<A:0x0000558a7485bd40 @name="j">,
 #<A:0x0000558a7485bd18 @name="k">,
 #<A:0x0000558a7485bcf0 @name="l">,
 #<A:0x0000558a7485bcc8 @name="m">]
Run Code Online (Sandbox Code Playgroud)

  • @LarsSchirrmeister 我不确定误导是否正确。如果您查看 [`Object#eql?`](https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F) 的文档,它确实指出:*”对于“eql?”返回“true”的任何一对对象,两个对象的“hash”值必须相等。因此任何覆盖“eql?”的子类也应该适当地覆盖“hash”。“*我认为这是记录这一点的适当位置,因此“Array#|”(联合)只需说明“使用“eql?”比较项目”似乎就足够了 (4认同)
  • `A = Struct.new(:name)` 为您提供与 Class 相同的行为(它附带 `initialize`、`eql?` 和 `hash`)。 (3认同)