On Sat, 11 Dec 2004 17:20:46 -0500, zuzu <sean.zuzu / gmail.com> wrote:
> here is how jim was thinking of it.  he seems to love mixing in
> Enumerable for all kinds of fun dataflow tricks, and that seemed not
> such a bad idea to me.
> 
> but don't let this stifle your creativity thinking about postfix /
> concatenative combinators this time around.  (the terminology of
> "concatenative combinators" first found me by way of the Forth
> language, which then influenced the Toy language.  feel free to also
> browse jim cunningham's excellent programming language wiki at
> c2.com).

er, ward cunningham.  got jim on the brain. :p  sorry ward.

> From: Jim Weirich <jim / weirichhouse.org>
> Reply-To: ruby-talk / ruby-lang.org
> To: ruby-talk ML <ruby-talk / ruby-lang.org>
> Date: Fri, 27 Aug 2004 11:07:09 +0900
> Subject: Re: python generators to ruby closures, help
> 
> William Morgan wrote:
> > Yes. I don't think you want the Generator class at all then. As the
> > email you quoted said, Python generators are a solution to a problem
> > that Ruby doesn't have. Generators have a place in Ruby but this ain't
> > it.
> >
> > From what I can tell, all the functionality they build up in the Python
> > example is just so that they can do thing that in Ruby can be done
> > neatly with iterators, like this:
> 
> As William pointed out, enumerator pipes can be implemented in Ruby
> without resorting to continuations, and that the current enumerator
> syntax is very pipe-like in many ways.
> 
> However, one thing the Python version handles that enumerator chaining
> does not is an infinite enumerator.  In the python examples, we have
> the following construct:
> 
>    Ints() | firstTen | oddNotDivBy3
> 
> Ints() is a generator that will generate all the integers (given
> enough time and space).  The next filter "firstTen" only takes the
> first 10 of those numbers.  Since the generator chain only takes a
> number from a generator when it is needed, the Ints() generator is
> never asked for any more than 10 number.
> 
> We can do the same with Ruby enumerators.  Imagine a OddNumberFilter
> object that implements a each method like this ...
> 
>   class OddNumberFilter
>     include Enumerable
>     attr_accessor :source
>     def each
>       @source.each { |n| yield(n) if (n % 2) != 0 }
>     end
>   end
> 
> It uses a source enumerable to generate numbers and only yields when
> it finds an odd number.  We can use it like this ...
> 
>    odds_only = OddNumberFilter.new
>    odds_only.source = (1..10)
>    odds_only.to_a              # => [1, 3, 5, 7, 9]
> 
> Since a filter can take _any_ enumerable as a source, it can even take
> other filters.  Let's generalize our OddNumberFilter class to a
> CondFilter class that takes a condition and only passes items that
> pass the condition.
> 
>   class CondFilter
>     include Enumerable
>     attr_accessor :source
>     def initialize(&block)
>       @block = block
>     end
>     def each
>       @source.each { |it| yield(it) if @block.call(it) }
>     end
>   end
> 
> Now we create a chain of filters ...
> 
>   odds_only = CondFilter.new { |n| (n % 2) != 0 }
>   odds_only.source = (1..10)
> 
>   divisible_by_three = CondFilter.new { |n| (n % 3) == 0 }
>   divisible_by_three.source = odds_only
> 
>   divisible_by_three.to_a    #=> [3, 9]
> 
> It works, but it is ugly.  So lets create a little syntactic sugar to
> get this to work ...
> 
>   module Enumerable
>     def |(filter)
>       filter.source = self
>       filter
>     end
>   end
> 
>   odds_only = CondFilter.new { |n| (n%2) != 0 }
>   divisible_by_three = CondFilter.new { |n| (n%3) == 0 }
> 
>   pipe = (1..10) | odds_only | divisible_by_three
>   pipe.to_a       # => [3, 9]
> 
> With this foundation, handling infinite enumerations are easy ...
> 
>   class Ints
>     include Enumerable
>     def initialize
>       @n = 0
>     end
>     def each
>       loop do
>         yield @n
>         @n += 1
>       end
>     end
>   end
> 
> And a class to take only so many items ...
> 
>   class Take
>     include Enumerable
>     attr_accessor :source
> 
>     def initialize(limit)
>       @limit = limit
>     end
>     def each
>       n = 0
>       @source.each do |it|
>         n += 1
>         break if n > @limit
>         yield it
>       end
>     end
>   end
> 
>   ( Ints.new | Take.new(5) ).to_a      # => [0, 1, 2, 3, 4]
> 
> From this point the File generator and the other stuff in the Python
> example should be obvious.
> 
> Here's a fun puzzle.  Using filters, create a Sieve of Erasothanes
> filter that, given an input of Ints.new, will generate a series of
> prime numbers.  In other words, the code ...
> 
>   ( Ints.new | Primes.new | Take.new(100) ).to_a
> 
> will create an array of the first 100 prime numbers.
> 
> I've attached full examples and test suites for all the the above
> (although factored a bit differently), so don't peek if you want to work
> out the Primes puzzle ...
> 
> --
> -- Jim Weirich    jim / weirichhouse.org     http://onestepback.org
> -----------------------------------------------------------------
> "Beware of bugs in the above code; I have only proved it correct,
> not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)