Issue #8840 has been updated by marcandre (Marc-Andre Lafortune).


akr (Akira Tanaka) wrote:
> Would??you??explain??the??incompleteness??concretely?

Sure. With your code above:

    e.drop2(40).map(&:odd?) # => [true, false]
    # expected lazy enumerator, as with original drop:
    e.drop(40).map(&:odd?)  # => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..42>:drop(40)>:map> 

Here is another implementation using `with_state` that returns a lazy enumerator:

    class Enumerator::Lazy
      def drop3(n)
        Lazy.new(with_state(remain: n)) do |y, (state, v)|
          if state[:remain] == 0
            y.yield v
          else
            state[:remain] -= 1
          end
        end
      end
    end

This implementation doesn't look so bad. It's probably quite a bit slower than using a Yielder#state method though.
----------------------------------------
Feature #8840: Yielder#state
https://bugs.ruby-lang.org/issues/8840#change-42216

Author: marcandre (Marc-Andre Lafortune)
Status: Feedback
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: core
Target version: current: 2.1.0


Defining an Enumerator that require a state is currently troublesome. For example, it is not really possible to define an equivalent of Lazy#drop in Ruby without making an assumption on the implementation.

To address this, I propose that we

(a) guarantee that a new Yielder object will be given for each enumeration
(b) add a 'state' attribute to Yielder.

This way, one could implement Lazy#drop in a way similar to:

  class Enumerator::Lazy < Enumerator
    def drop(n)
      n = n.to_i
      Lazy.new(self) do |yielder, *values|
        yielder.state ||= n
        if yielder.state > 0
          yielder.state -= 1
        else
          yielder.yield(*values)
        end
      end
    end
  end

Note that (a) is currently true for Ruby MRI, JRuby and Rubinius, but it is not explicit in the documentation.


-- 
http://bugs.ruby-lang.org/