From: zuzu Date: 2004-12-12T07:56:18+09:00 Subject: Re: puts / print as method not keyword? On Sat, 11 Dec 2004 17:20:46 -0500, zuzu 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 > Reply-To: ruby-talk@ruby-lang.org > To: ruby-talk ML > 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)