Internal Iterators
If you think about it, the purpose of an iterator is to introduce your code to each sub-object of an aggregate object. Traditional external iterators do so by providing a long grappling hook, the iterator object, that you can use to pull the sub-objects out of the aggregate without getting messily involved in the aggregate details. But by using a code block, you can pass your logic down into the aggregate. The aggregate can then call your code block for each of its sub-objects. Because all of the iterating action occurs inside the aggregate object, the code block-based iterators are called internal iterators.
Building an internal iterator for arrays is very easy—we just define a method that calls (via yield) the passed-in code block for each element:1
def for_each_element(array) i = 0 while i < array.length yield(array[i]) i += 1 end end
To use our internal iterator, we hang a code block on the end of the method call:
a = [10, 20, 30] for_each_element(a) {|element| puts("The element is #{element}")}
It turns out that we don’t really need for_each_element—the Array class sports a fine iterator method call called each. Just like our for_each_element method, each takes a one-parameter code block and calls that code block for each element in the array:
a.each {|element| puts("The element is #{element}")}
Run either version of the preceding code and you will get this output:
The element is 10 The element is 20 The element is 30
The each method is the explanation for all of those funny-looking each loops that you have been seeing in this book. Those loops are not, in fact, actual “built-into-the-language” loops, but rather applications of internal iterators.