覆盖#each;我可以通过#map,#select等传递它吗?

时间:2014-04-21 12:26:19

标签: ruby enumerable

我写了自己的Tree课程,其中包括EnumerableTree然后提供#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

3 个答案:

答案 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 }