如何创建一个在迭代后执行某些操作的枚举器?

时间:2014-03-31 03:09:54

标签: ruby block enumerator

如何创建一个可选择占用块的枚举器?我想用一些参数和一个可选块调用一些方法foo。如果使用块调用它,则应该在块上进行迭代,并且应该在其上执行某些操作,包括给定的参数。例如,如果foo采用单个数组参数,则对其应用map给定的块,并返回应用于它的join的结果,然后它看起来像:

foo([1, 2, 3]){|e| e * 3}
# => "369"

如果在没有块的情况下调用它,它应该返回一个枚举器,Enumerator的方法(例如with_index)应该能够应用,并以相应的方式执行块:

enum = foo([1, 2, 3])
# => Enumerator
enum.with_index{|e, i| e * i}
# => "026"

我使用条件定义foo以查看是否给出了块。很容易实现给出块的情况,但返回枚举器的部分更难。我想我需要实现MyEnum的子类Enumerator,并使foo返回它的实例:

def foo a, &pr
  if pr
    a.map(&pr).join
  else
    MyEnum.new(a)
  end
end

class MyEnum < Enumerator
  def initialize a
    @a = a
    ...
  end
  ...
end

但是调用MyEnum.new会发出警告消息:Enumerator.new without a block is deprecated; use Object#to_enum。如果我使用to_enum,我认为它会返回一个普通的Enumerator实例,而不是内置特定功能的MyEnum。最重要的是,我不知道如何实现{ {1}}首先。我该如何实现这样的枚举器?或者,正确的方法是什么?

1 个答案:

答案 0 :(得分:3)

你可以这样做。

def foo a, &pr
  if pr
    a.map(&pr).join
  else
    o = Object.new
    o.instance_variable_set :@a, a
    def o.each *y
      foo @a.map { |z| yield z, *y } { |e| e }
    end
    o.to_enum
  end
end

然后我们

enum = foo([1,2,3])
enum.each { |x| 2 * x } # "246"

enum = foo([1,2,3])
enum.with_index { |x, i| x * i } # "026"

灵感来自Enumerator documentation。请注意,您对此类枚举器的所有期望都要求保留,因为.to_enum负责处理所有这些问题。 enum现在是合法的Enumerator

enum.class # Enumerator