What's the best way to return an Enumerator::Lazy when your class doesn't define #each?

此生再无相见时 提交于 2019-11-30 21:45:37

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

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...]

If they actually need a lazy enumerator, they can call lazy themselves:

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

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?
    #...

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:

  • All methods of Enumerable assume an each, not just lazy.

  • 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.

  • 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.

  • 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!