Ruby定制包括?为了集

时间:2015-01-30 06:27:02

标签: ruby hash set comparator

我定义了一个名为Point的类,如下所示:

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

  attr_accessor :x, :y
end

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

(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)。

谢谢!

5 个答案:

答案 0 :(得分:3)

来自documentation for Set

  

Set使用哈希作为存储,因此您必须注意以下几点:

     
      
  • 元素的相等性是根据Object#eql确定的?和Object #hash。
  •   
  • Set假定每个元素的标识在存储时不会更改。修改集合的元素会将集合渲染为不可靠状态。
  •   
  • 当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已被冻结。
  •   

因此,您需要覆盖并实施eql?类上的hashPoint方法。

答案 1 :(得分:0)

我想,你需要通过维护变量然后迭代集合来使用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

答案 2 :(得分:0)

这取决于你想如何实现目标。您可以覆盖eql?上的hashPoint(根据@TomDalling的建议),也可以覆盖include?本地类的@obstacles

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

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

请注意,您是否希望Point在生命周期内更改其值,应使用后一种方法(根据“Set假设每个元素的标识在存储时不会更改”)。 )

答案 3 :(得分:0)

有一个简单的解决方案:让你的Point成为一个班轮:

Point = Struct.new :x, :y

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

答案 4 :(得分:0)

(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 !!”这不是单线,而是