我写了自己的Tree
课程,其中包括Enumerable
。 Tree
然后提供#each
功能。因此,它能够自动获取Enumerable
,#map
,#select
等所有#find
函数。到目前为止,这一切都在我的代码中有效。
这是问题所在。当我为#each
编写Tree
时,我给了#each
一个参数,该参数是要使用的树遍历算法的名称,例如:pre_order
或:breadth_first
。但现在,当我调用#map
或#inject
或#any?
等时,它只能使用默认的遍历算法。有什么方法可以通过其他Enumerable
函数传递这个参数吗?这是我的标准;
Enumerable
函数使用任何遍历算法。这非常重要,因为树可以针对不同的算法具有非常不同的性能。Enumerable
函数来将此参数传递给#each
;这违背了该模块的目的。这是我的代码的缩写版本;
class Tree
include Enumerable
...
# Overwrite #each, and give it the algorithm argument.
def each(algorithm = :pre_order, &block)
if TRAVERSAL_ALGORITHMS.include? algorithm
self.send(algorithm, &block)
else
self.method_missing(algorithm)
end
end
def pre_order(&block)
yield self
self.branches.each do |branch|
branch.pre_order(&block)
end
end
def post_order(&block)
...
end
def breadth_first(&block)
...
end
end
我想打电话给这样的事情;
tree.find(13, :breadth_first)
tree.any?(:post_order) do |node|
node.root >= 10
end
答案 0 :(得分:1)
我很傻。
方法#enum_for
在这里给了我所有的力量。我可以实现查理的
tree.breadth_first.find(13)
通过添加传统的线
return self.enum_for(__method__) unless block_given?
在我的每个遍历方法中。 tree.breadth_first
将返回Enumerator
,根据广度优先算法进行枚举;调用的任何Enumerable
方法都将在内部使用该枚举。
答案 1 :(得分:0)
可能的答案:设置要使用哪种算法的实例变量。然后代码看起来像这样;
class Tree
include Enumerable
attr_accessor :traversal_algorithm
def initialize(...)
@traversal_algorithm = :pre_order
...
end
...
def each(&block)
if TRAVERSAL_ALGORITHMS.include? @algorithm
self.send(@algorithm, &block)
else
self.method_missing(@algorithm)
end
end
end
并且算法将设置为这样;
tree.traversal_algorithm = :breadth_first
tree.find(13)
tree.traversal_algorithm = :post_order
tree.any? do |node|
node.root >= 10
end
这似乎是一个可能很糟糕的主意。
答案 2 :(得分:0)
我会遵循查理的建议并使用代理对象:
class Tree
class PreOrderEnumerator
include Enumerable
attr_reader :tree
def initialize(tree)
@tree = tree
end
def each(&block)
yield tree
tree.branches.each do |branch|
branch.pre_order(&block)
end
end
end
def pre_order
PreOrderEnumerator.new(self)
end
end
然后您可以这样使用:
tree.pre_order.any? {|node| node.root > 10 }