当您的类未定义#each时,返回Enumerator :: Lazy的最佳方法是什么? [英] What's the best way to return an Enumerator::Lazy when your class doesn't define #each?
问题描述
Enumerable#lazy
依赖于您提供的#each
方法.如果您的可枚举没有#each
方法,则不能使用#lazy
.现在,Kernel#enum_for
和#to_enum
可以灵活地指定#each
以外的枚举方法:
Enumerable#lazy
relies on your enumerable providing an #each
method. If your enumerable doesn't have an #each
method you can't use #lazy
. Now Kernel#enum_for
and #to_enum
provide the flexibility to specify an enumeration method other than #each
:
Kernel#enum_for(method = :each, *args)
但是#enum_for
和朋友总是构造普通的(非惰性)枚举器,而不是Enumerator::Lazy
.
But #enum_for
and friends always construct plain (non-lazy) enumerators, never Enumerator::Lazy
.
我看到Ruby 1.9.3中的Enumerator
提供了类似的#new形式:
I see that Enumerator
in Ruby 1.9.3 offers this similar form of #new:
Enumerator#new(obj, method = :each, *args)
不幸的是,在Ruby 2.0中该构造函数已被完全删除.我也不认为它在Enumerator::Lazy
上根本无法使用.所以在我看来,如果我有一个带有方法的类,我想为其返回一个惰性枚举器,如果该类没有#each
,那么我必须定义一些确实定义了#each
的帮助器类.
Unfortunately that constructor has been completely removed in Ruby 2.0. Also I don't think it was ever available at all on Enumerator::Lazy
. So it seems to me that if I have a class with a method I want to return a lazy enumerator for, if that class has no #each
then I have to define some helper class that does define #each
.
例如,我有一个Calendar
类.对我来说,提供从所有时间开始的每个日期的枚举对我来说真的没有任何意义. #each
将是无用的.相反,我提供了一种从开始日期开始(延迟)枚举的方法:
For instance, I've got a Calendar
class. It doesn't really make sense for me to offer to enumerate every date from the beginning of all time. An #each
would be useless. Instead I offer a method that enumerates (lazily) from a starting date:
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
它可以工作,但是感觉很沉.也许我应该继承Enumerator::Lazy
的子类,并定义一个类似Enumerator
弃用的构造函数.你觉得呢?
It works but it feels heavy. Maybe I should subclass Enumerator::Lazy
and define a constructor like that deprecated one from Enumerator
. What do you think?
推荐答案
我认为您应该使用to_enum
返回正常的Enumerator
:
I think you should return a normal Enumerator
using 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
这是大多数红宝石学家所期望的.即使它是无限的Enumerable
,它仍然可以使用,例如:
This is what most rubyists would expect. Even though it's an infinite Enumerable
, it is still usable, for example:
Calendar.new.each_from(1.year.from_now).first(10) # => [...first ten dates...]
如果他们实际上需要一个惰性枚举器,则可以自己调用lazy
:
If they actually need a lazy enumerator, they can call lazy
themselves:
Calendar.new.each_from(1.year.from_now)
.lazy
.map{...}
.take_while{...}
如果您确实想要返回一个惰性枚举器,则可以从您的方法中调用lazy
:
If you really want to return a lazy enumerator, you can call lazy
from you method:
# ...
def each_from(first)
return to_enum(:each_from, first).lazy unless block_given?
#...
不过,我不建议这样做,因为这是意外的(IMO),可能会造成过大的杀伤力,并且性能会降低.
I would not recommend it though, since it would be unexpected (IMO), could be an overkill and will be less performant.
最后,您的问题中存在一些误解:
Finally, there are a couple of misconceptions in your question:
-
Enumerable
的所有方法都假定为each
,而不仅仅是lazy
.
All methods of
Enumerable
assume aneach
, not justlazy
.
如果愿意,可以定义一个需要参数的each
方法,并包含Enumerable
. Enumerable
的大多数方法都行不通,但是each_with_index
和其他一些方法会转发参数,因此可以立即使用.
You can define an each
method that requires a parameter if you like and include Enumerable
. Most methods of Enumerable
won't work, but each_with_index
and a couple of others will forward arguments so these would be usable immediately.
没有Enumerator.new
消失了,因为to_enum
是应该使用的.注意,块形式仍然存在. Lazy
也有一个构造函数,但这是从现有的Enumerable
开始的.
The Enumerator.new
without a block is gone because to_enum
is what one should use. Note that the block form remains. There's also a constructor for Lazy
, but it's meant to start from an existing Enumerable
.
您声明to_enum
从未创建过惰性枚举器,但这并非完全正确. Enumerator::Lazy#to_enum
专门用于返回惰性枚举器. Enumerable
上任何调用to_enum
的用户方法都将使惰性枚举数保持惰性.
You state that to_enum
never creates a lazy enumerator, but that's not entirely true. Enumerator::Lazy#to_enum
is specialized to return a lazy enumerator. Any user method on Enumerable
that calls to_enum
will keep a lazy enumerator lazy.
这篇关于当您的类未定义#each时,返回Enumerator :: Lazy的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!