Ruby 自定义包含?对于集合

Ruby custom include? for set

我定义了一个名为 Point 的 class 如下:

class Point
  def initialize (x,y)
    @x, @y = x, y
  end

  attr_accessor :x, :y
end

在我项目的另一部分,我生成了随机点对象并将它们放入一个集合中。然而,集合中的点必须是唯一的:

(1..numObstacles).each do |n|
    p = Point.new(rand(minX..maxX), rand(minY..maxY))
      if !@obstacles.include?(p)
        @obstacles.add(p)
    end
end

我知道这不适用于唯一性约束,因为在循环的每次迭代中,p 都是不同的对象,并且 Ruby 不知道如何比较两个点对象。但是我无法想出/找到一种正确的方法来覆盖包含?方法或提供自定义比较器(如 Java)。

谢谢!

我想,您需要通过维护一个变量然后迭代该集合来使用 hack 类的东西。

(1..numObstacles).each do |n|
    p, cntr = Point.new(rand(minX..maxX), rand(minY..maxY)), false
    @obstacles.each { |po| cntr = true if po.x == p.x && po.y == p.y }
    @obstacles.add(p) unless cntr
    end
end

我们正在做的是迭代集合,然后将 cntr 设置为 true,如果我们找到重复项,然后我们将其添加到 @obstacles

来自documentation for Set

Set uses Hash as storage, so you must note the following points:

  • Equality of elements is determined according to Object#eql? and Object#hash.
  • Set assumes that the identity of each element does not change while it is stored. Modifying an element of a set will render the set to an unreliable state.
  • When a string is to be stored, a frozen copy of the string is stored instead unless the original string is already frozen.

因此您需要在 Point class.

上覆盖并实现 eql?hash 方法

这取决于您想如何实现目标。您可以在 Point 上覆盖 eql?hash(如@TomDalling 所建议),或者您可以在 @obstacles 特征类上覆盖 include?

class << @obstacles 
   def include? other
     Point === other && self.any? { |p| 
      other.x == p.x && other.y == p.y 
     }   
   end 
end

无论您只想处理 Point,前一种方法更好。后者更具可定制性,因为您随后可以检查完全不同的对象。

请注意,无论您是否希望 Points 在生命周期中更改它们的值,都应使用后一种方法(根据“Set assumes that identity of each element does not change while it is stored ”)

有一个简单的解决方案:将您的 Point 设为单线:

Point = Struct.new :x, :y

然后自动定义所有必要的方法(#hash、#eql?)。

(1..numObstacles).each do |n|
    p = Point.new(rand(minX..maxX), rand(minY..maxY))
      @obstacles << p
      @obstacles.uniq!
    end
end

or if you need the number of things in the array to be a certain amount:

until @obstacles.length == numObstacles do |n|
    p = Point.new(rand(minX..maxX), rand(minY..maxY))
     @obstacles << p
     @obstacles.uniq!
    end
end

另外 numObstacles 应该是 num_obstacles。我喜欢这种方式,因为它是声明式的。 6/10 的程序员看到一个结构就去 "Awww man!!" 这不是单行,而是