From: Tom Sawyer Date: 2002-08-06T05:40:50+09:00 Subject: Re: Super-iterator? (long) hal, your my hero! i, and undoubtedly others, have brought this up before. i think this is a much needed tool and could very well stand being a standard ruby feature. although i think that the "full approach" would be to split out the iterating aspects of Enumberable into a seperate module called Iterable. Enumberable then becomes a mix of Countable and Iterable. nice work. i'm going to add this to my main library. let me know if you improve on it. thanks, transami On Mon, 2002-08-05 at 14:20, Hal E. Fulton wrote: > Here's an idea for you. I've worked on it a couple of days. > > Please feel free to critique the concept and/or the code. > > The code can definitely be improved. It's ugly and has > limitations and probably has bugs. > > Motivation: > > 1. Sometimes I want to know whether I'm in the first (or last) > iteration of an iterator block. > 2. Sometimes I want to do things "alternately" (i.e., for every > other iteration). > 3. I'm a tiny bit displeased by the difference between each and > each_with_index. I'd like them to be done by one thing. > 4. Sometimes I want to change an item in an array, for instance. > each won't allow this; I have to use each_with_index and manually > index into the array. > 5. Sometimes an ordinary iterator seems inadequate; I want something > that can grab the next entry on demand, without waiting for the > loop to run around again. > > The solution: > > 1. Create an IteratorVar class that stores all the information used > in the looping. > 2. Give it first? and last? methods. > 3. Give it odd? and even? methods. > 4. Give it an accessor that can retrieve the value and is even able > to replace the value in the original list. > 5. Give it a 'next' method that will update the counter and return > the relevant object. > 6. Put 'iterator' into Enumerable (actually, at this point it should > probably NOT go into Enumerable) which wraps it all in a big while > loop that does a yield. > 7. The yielded item will be an IteratorVar. > > Bugs and limitations: > > 1. It pretty much only works for arrays now. The "list = self.entries" > line created a new array, so I couldn't store items back into the array > with #value=. So I changed it to "list = self" -- meaning that the > object *must* have [] and be indexed by an integer. So that's pretty > much an Array. I've left it this way to illustrate the concept. > 2. Obviously things like files are out of the question at the moment. > > Code and output below. > > Cheers, > Hal Fulton > > > > class IteratorVar > attr_accessor :count_, :max_ > attr_reader :value > attr_reader :skip? > attr_writer :skip > > def initialize(index,max,list) > @count_ = index > @max_ = max > @value = list[index] > @skip = false > @list_ = list > end > > def first? > @count_ == 0 > end > > def last? > @count_ >= @max_ > end > > def odd? > @count_ % 2 == 1 > end > > def even? > @count_ % 2 == 0 > end > > def next > @count_ += 1 > @value = @list_[@count_] > @skip = true > self.dup > end > > def value=(val) > @value = val > @list_[@count_] = val > end > > def inspect > @value.inspect > end > > def to_s > @value.to_s > end > > def to_str > @value.to_str > end > > end > > module Enumerable > > def iterate > low = 0 > high = self.size-1 > list = self > i = low > x = IteratorVar.new(i,high,list) > while i <= high > x.count_ = i > x.value = list[i] > yield x > if x.skip? > i = x.count_ > x.skip = false > else > i = x.count_ += 1 > end > end > end > > end > > > a = %w[zero one two three four] > > a.iterate do |x| > puts "Value is #{x} " > puts " Index is odd " if x.odd? > puts " Index is even" if x.even? > puts " First item!" if x.first? > puts " Last item!" if x.last? > end > > puts > > # Same array a -- capitalize every other entry > > a.iterate do |x| > if x.odd? > x.value = x.value.upcase > end > end > > a.each {|x| puts x} > > puts > > # Problem: For a list of items, grab the "parent" > # and the fixed number of children following. A > # parent is a single letter -- if it's a vowel > # [aeiou] it has a single child; a consonant will > # have three children > > puts > > items = %w[ b 0 1 2 a 3 c 3 4 5 d 6 7 8 e 9 ] > > items.iterate do |item| > parent = item.value > if parent =~ /[aeiou]/ > puts "#{parent} -> #{item.next}" > else > arr = [] > 3.times { arr << item.next} > puts "#{parent} -> #{arr}" > end > end > > # Output... > =begin > Value is zero > Index is even > First item! > Value is one > Index is odd > Value is two > Index is even > Value is three > Index is odd > Value is four > Index is even > Last item! > > zero > ONE > two > THREE > four > > > b -> 012 > a -> 3 > c -> 345 > d -> 678 > e -> 9 > =end > > > -- ~transami