
Included with the Ruby distribution are the generator library and the enumerator extension — both useful tools when ordinary iteration doesn’t quite measure up.
The enumerator extension is simple in concept: create a new Enumerable object given an object and a method of that object to be used as an iterator. For example, if we add an each_even iterator to the Array class to iterate over every element with an even numbered index, we can use enumerator to create enumerable versions of an array object that use each_even as the iterator:
require 'enumerator'
class Array
def each_even
self.each_with_index do|el,i|
yield el if i % 2 == 0
end
end
end
arr = ['a','b','c','d','e','f','g','h']
enum = Enumerable::Enumerator.new(arr, :each_even)
ev = enum.map {|x| x + x}
p ev #=> ["aa", "cc", "ee", "gg"]
In addition to the constructor above, the following convenience functions are added to the Object class:
to_enum(:iter, *args) enum_for(:iter, *args)
The Enumerable module is also extended with five additional methods:
each_slice(n) # iterates over non-overlapping chunks of size n
enum_slice(n) # new enumerator object using :each_slice(n)
('a'..'m').each_slice(4) {|sl| p sl}
# produces:
["a", "b", "c", "d"]
["e", "f", "g", "h"]
["i", "j", "k", "l"]
["m"]
each_cons(n) # iterates over successive chunks of size n
enum_cons(n) # new enumerator using :each_cons(n)
('a'..'m').each_cons(4) {|sl| p sl}
# produces:
["a", "b", "c", "d"]
["b", "c", "d", "e"]
["c", "d", "e", "f"]
["d", "e", "f", "g"]
["e", "f", "g", "h"]
["f", "g", "h", "i"]
["g", "h", "i", "j"]
["h", "i", "j", "k"]
["i", "j", "k", "l"]
["j", "k", "l", "m"]
enum_with_index # new enumerator using :each_with_index
The generator library generates external iterators from either blocks or Enumerable objects (in the latter case, the :each iterator is externalized).
require 'generator'
arr = ('a' .. 'm')
gen = Generator.new(arr)
while gen.next?
p gen.next
end
This makes iterating over multiple objects relatively easy. However, the generator library also provides the SyncEnumerator class which makes multiple iteration a breeze:
require 'generator'
a = (4..5)
b = ['a',nil,'c']
c = ['x','y','x']
enum = SyncEnumerator.new(a, b, c)
enum.each {|row| p row}
puts '---'
table = [ [1,2,3], [4,5,6], [7,8,9] ]
cols = SyncEnumerator.new(*table)
cols.each {|col| p col}
# produces:
[4, "a", "x"]
[5, nil, "y"]
[nil, "c", "x"]
---
[1, 4, 7]
[2, 5, 8]
[3, 6, 9]
All of which just goes to show: There’s more than one way to iterate an enumerable.
__END__