当你的类没有定义#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
方法,并包含Enumerable
。Enumerable
大多数方法都不起作用,但each_with_index
和其他几个方法都会转发参数,因此这些方法可以立即使用。 -
没有块的
Enumerator.new
已经消失,因为to_enum
是应该使用的。 请注意,块forms仍然存在。 还有一个Lazy
的构造函数,但它的意思是从现有的Enumerable
开始。 -
你声明
to_enum
永远不会创建一个懒惰的枚举器,但这并不完全正确。Enumerator::Lazy#to_enum
专门用于返回一个惰性枚举器。Enumerable
上调用to_enum
任何用户方法都会使懒惰的枚举器保持惰性。