On Tue, Nov 04, 2008 at 03:41:27PM +0900, Yukihiro Matsumoto wrote:
> See [ruby-core:19691].  It's an issue with #next.

Thanks: I see that the 'next' API for calling enumerators is less powerful
compared to each { ...block... }, since the block can return a value.

But when does #inject actually depend on #next, rather than #each?

Let me see if I can explain what I'm trying to say. ISTM there are now
several ways you can write an iterator.


(1) The traditional iterator.

  class Fib
    def initialize(a=1,b=1)
      @a, @b = a, b
    end
    def each
      a, b = @a, @b
      yield a
      while true
        yield b
        a, b = b, a+b
      end
    end
  end

  # Simple call
  Fib.new.each { |x| puts x }

  # if you 'include Enumerable' in the class then you can also do
  #   puts Fib.new.take(10)

  # External iteration
  fib = Fib.new.to_enum
  while true; puts fib.next; end

  # Enumerator is itself Enumerable
  puts fib.take(10)


(2) The Enumerator block way.

  fib = Enumerator.new { |y|
    a = b = 1
    loop {
      y << a
      a, b = b, a + b
    }
  }

  # Simple call
  puts fib.each { |x| puts x }

  # External iteration
  fib.rewind
  while true; puts fib.next; end


(3) As a custom class which implements external iteration directly. These
are pretty ugly, as they're unlikely to be thread-safe.

  class Fibber
    def initialize(a=1, b=1)
      @a1, @b1 = a, b
      rewind
    end
    def rewind
      @a, @b = @a1, @b1
    end
    def next
      res = @a
      @a, @b = @b, @a+@b
      res
    end
  end

  f = Fibber.new
  10.times { puts f.next }
  f.rewind
  10.times { puts f.next }

  # You can turn this into internal iteration; I don't know if there is
  # a method for this in the core language already

  def ext_to_int(ext_iterator)
    Enumerator.new do |y|
      ext_iterator.rewind
      begin
        while true
          y << ext_iterator.next
        end
      rescue StopIteration
      end
    end
  end

  f2 = ext_to_int(f)
  f2.each { |x| puts x }


Now, as you can see, it is possible to adapt the "each" pattern to the
"next" pattern, and vice versa.

Where I'm going is this: in what way does the Ruby core depend on the "next"
pattern? It may be useful for users to be able to *use* the "next" pattern
as a way of pulling values from an existing iterator.

But Enumerable#inject is a built-in Ruby method, and as far as I can see it
only needs to call 'each' on the underlying object. In the (unusual) case
that you had an external-only iteration object which you wished to call
#inject on, then you can wrap it as in example (3) above.

However, I feel that I have missed an important point entirely, and so I'd
be very happy to be enlightened on this.

Regards,

Brian.

P.S. I'm still missing the point of enumerators created from 'map' or
'select' without blocks.

    a = (1..10)

    b = a.select

    # I believe this is the same as
    b = a.to_enum(:select)

    # Both execute b.each { ... } as a.select { ... }
    # But why is this useful?