我定义了一个名为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)。
谢谢!
答案 0 :(得分:3)
Set使用哈希作为存储,因此您必须注意以下几点:
- 元素的相等性是根据Object#eql确定的?和Object #hash。
- Set假定每个元素的标识在存储时不会更改。修改集合的元素会将集合渲染为不可靠状态。
- 当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已被冻结。
因此,您需要覆盖并实施eql?
类上的hash
和Point
方法。
答案 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?
上的hash
和Point
(根据@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 !!”这不是单线,而是