What is the best way to return Enumerator :: Lazy when your class does not define #each?

Enumerable#lazyrelies on your enumerated method #each. If your counter does not have a method #each, you cannot use #lazy. Now Kernel#enum_forthey #to_enumprovide the flexibility to specify an enumeration method other than #each:

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

But #enum_forfriends always build simple (non-lazy) listings, never Enumerator::Lazy.

I see that Enumeratorin Ruby 1.9.3 it offers the same form #new:

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

Unfortunately, this constructor is completely removed in Ruby 2.0. Also I do not think that it was generally available on Enumerator::Lazy. So it seems to me that if I have a class with a method, I want to return a lazy enumerator, if this class does not #each, then I have to define some helper class that defines #each.

For example, I have a class Calendar. It makes no sense for me to suggest listing every date from the beginning of all time. #eachwill be useless. Instead, I suggest a method that lists (lazily) from the start 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

And this class is EachFromas follows:

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

It works, but it feels hard. Maybe I should subclass Enumerator::Lazyand define a constructor that would be deprecated from Enumerator. What do you think?

+5
1

, 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

, . , 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 each, lazy.

  • each, , , Enumerable. Enumerable , each_with_index , .

  • Enumerator.new , to_enum - , . , . lazy, Enumerable.

  • , to_enum , . Enumerator::Lazy#to_enum . Enumerable, to_enum, .

+7

All Articles