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

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

 Kernel#enum_for(method = :each, *args) 

#enum_for和朋友总是构造普通(非懒惰)枚举器,而不是Enumerator::Lazy

我看到Ruby 1.9.3中的Enumerator提供了类似的#newforms:

 Enumerator#new(obj, method = :each, *args) 

不幸的是,在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 

并且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 

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

我认为你应该使用to_enum返回一个普通的Enumerator器:

 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 

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

 Calendar.new.each_from(1.year.from_now).first(10) # => [...first ten dates...] 

如果他们真的需要一个懒惰的枚举器,他们可以自己调用lazy

 Calendar.new.each_from(1.year.from_now) .lazy .map{...} .take_while{...} 

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

  # ... def each_from(first) return to_enum(:each_from, first).lazy unless block_given? #... 

我不会推荐它,因为它会出乎意料(IMO),可能是一种矫枉过正,性能会降低。

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

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

  • 如果您愿意,可以定义需要参数的each方法,并包含EnumerableEnumerable大多数方法都不起作用,但each_with_index和其他几个方法都会转发参数,因此这些方法可以立即使用。

  • 没有块的Enumerator.new已经消失,因为to_enum是应该使用的。 请注意,块forms仍然存在。 还有一个Lazy的构造函数,但它的意思是从现有的Enumerable开始。

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