From: "Hal E. Fulton" Date: 2002-08-06T05:20:12+09:00 Subject: Super-iterator? (long) 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