Yuh-Ruey Chen wrote:
> Yes, this is exactly what I'm looking for - the ability to chain
> iterators like this without creating intermediate arrays, hopefully
> using less memory.

I've researched this a bit more, and it seems that ruby 1.9 *does* 
support this model in a very smart way. Unfortunately the built-in 
Enumerable methods like #map, #select, #reject don't, but these are easy 
enough to synthesise. I started a new thread on that topic at 
http://www.ruby-forum.com/topic/169807

Anyway, if you want the following pattern

   generator --> filter --> filter ... --> consumer

you implement it as follows.

(1) The generator is any object which implements 'each' and yields the 
members of the (possibly infinite) collection.

class Fib
  include Enumerable
  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

(by including Enumerable, you gain a whole load of useful methods which 
in turn call your 'each' method)

(2) The consumer is an object which calls 'each' with a block that does 
something with the data yielded to it:

   obj.each { |i| puts i }

So far, all so ruby-1.8.

(3) The clever bit is the filter, and this only works in ruby-1.9. You 
create an Enumerator object with a block which processes the data.

  Enumerator.new do |y|
    obj.each do |thing|     # the filter INPUT
      ...
      y << otherthing       # the filter OUTPUT
    end
  end

The really clever bit is the "y << otherthing" line. By cunning use of 
Fibers (I believe), execution of the filter stops at this point, 
allowing the next filter along the chain to make use of the value as its 
input. Once all the filters along the chain have finished using this 
value, this filter carries on into the next iteration of its 'each' 
loop, to take the next input from upstream.

So here's a complete example with an infinite source, two filters, and a 
consumer. Notice that only the consumer decides when to terminate; 
before this point, everything is a potentially infinite series.

module Enumerable
  def lmap(&blk)
    Enumerator.new do |y|
      each do |e|
        y << blk[e]
      end
    end
  end

  def lselect(&blk)
    Enumerator.new do |y|
      each do |e|
        y << e if blk[e]
      end
    end
  end
end

class Fib
  include Enumerable
  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

Fib.new.lselect { |i| i % 2 == 0 }.lmap { |i| "<#{i}>" }.
  each_with_index { |i,cnt| puts i; break if cnt >= 20 }

$ ruby19 fib.rb
<2>
<8>
<34>
<144>
<610>
<2584>
<10946>
<46368>
<196418>
<832040>
<3524578>
<14930352>
<63245986>
<267914296>
<1134903170>
<4807526976>
<20365011074>
<86267571272>
<365435296162>
<1548008755920>
<6557470319842>
-- 
Posted via http://www.ruby-forum.com/.