当你的类没有定义#each时,返回Enumerator :: Lazy的最佳方法是什么?

Bil*_*ham 5 ruby enumerable lazy-evaluation ruby-2.0

Enumerable#lazy依赖于你的可枚举提供#each方法.如果您的枚举没有#each方法,则无法使用#lazy.现在Kernel#enum_for,#to_enum提供指定除以下之外的枚举方法的灵活性#each:

Kernel#enum_for(method = :each, *args)
Run Code Online (Sandbox Code Playgroud)

但是#enum_for,朋友总是构建普通(非懒惰)的普查员,从不Enumerator::Lazy.

我看到Enumerator在Ruby 1.9.3中提供了类似的#new形式:

Enumerator#new(obj, method = :each, *args)
Run Code Online (Sandbox Code Playgroud)

不幸的是,在Ruby 2.0中已经完全删除了构造函数.此外,我认为它根本不可用Enumerator::Lazy.所以在我看来,如果我有一个带有方法的类,我想返回一个惰性枚举器,如果该类没有,#each那么我必须定义一些定义的辅助类#each.

例如,我有一Calendar堂课.从一开始就提出列举每一个日期对我来说真的没有意义.一个#each将是无用的.相反,我提供了一个从开始日期(懒惰)枚举的方法:

  class Calendar
    ...
    def each_from(first)
      if block_given?
        loop do
          yield first if include?(first)
          first += step
        end
      else
        EachFrom.new(self, first).lazy
      end
    end
  end
Run Code Online (Sandbox Code Playgroud)

那个EachFrom班看起来像这样:

class EachFrom
  include Enumerable
  def initialize(cal, first)
    @cal   = cal
    @first = first
  end
  def each
    @cal.each_from(@first) do |yielder, *vals|
      yield yielder, *vals
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

它有效,但感觉很重.也许我应该子类化Enumerator::Lazy并定义一个类似于那个被弃用的构造函数Enumerator.你怎么看?

Mar*_*une 7

我认为你应该恢复正常Enumerator使用to_enum:

class Calendar
  # ...
  def each_from(first)
    return to_enum(:each_from, first) unless block_given?
    loop do
      yield first if include?(first)
      first += step
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这是大多数红宝石家所期望的.即使它是无限的Enumerable,它仍然可用,例如:

Calendar.new.each_from(1.year.from_now).first(10) # => [...first ten dates...]
Run Code Online (Sandbox Code Playgroud)

如果他们真的需要一个懒惰的调查员,他们可以lazy自称:

Calendar.new.each_from(1.year.from_now)
  .lazy
  .map{...}
  .take_while{...}
Run Code Online (Sandbox Code Playgroud)

如果你真的想要返回一个懒惰的枚举器,你可以lazy从你的方法调用:

  # ...
  def each_from(first)
    return to_enum(:each_from, first).lazy unless block_given?
    #...
Run Code Online (Sandbox Code Playgroud)

我不会推荐它,因为它会出乎意料(IMO),可能是一种矫枉过正,而且效率会降低.

最后,你的问题中存在一些误解:

  • 所有方法都Enumerable假设一个each,而不仅仅是lazy.

  • 您可以根据each需要定义需要参数的方法Enumerable.大多数方法Enumerable都不起作用,但是each_with_index其他几个方法会转发参数,因此这些方法可以立即使用.

  • Enumerator.new不使用块就没了,因为to_enum是一个应该使用什么.请注意,块形式仍然存在.还有一个构造函数Lazy,但它的意思是从现有的开始Enumerable.

  • 你声明to_enum永远不会创建一个懒惰的枚举器,但这并不完全正确.Enumerator::Lazy#to_enum专门用于返回惰性枚举器.Enumerable该调用的任何用户方法to_enum都会使懒惰的枚举器保持惰性.