Iterators and Enumerable Objects

Although while, until, and for loops are a core part of the Ruby language, it is probably more common to write loops using special methods known as iterators. Iterators are one of the most noteworthy features of Ruby, and examples such as the following are common in introductory Ruby tutorials:

3.times { puts "thank you!" }  # Express gratitude three times
data.each {|x| puts x }        # Print each element x of data
[1,2,3].map {|x| x*x }         # Compute squares of array elements
factorial = 1                  # Compute the factorial of n
2.upto(n) {|x| factorial *= x }

The times, each, map, and upto methods are all iterators, and they interact with the block of code that follows them. The complex control structure behind this is yield. The yield statement temporarily returns control from the iterator method to the method that invoked the iterator. Specifically, control flow goes from the iterator to the block of code associated with the invocation of the iterator. When the end of the block is reached, the iterator method regains control and execution resumes at the first statement following the yield. In order to implement some kind of looping construct, an iterator method will typically invoke the yield statement multiple times. Figure 5-1 illustrates this complex flow of control. Blocks and yield are described in detail in Blocks below; for now, we focus on the iteration itself rather than the control structure that enables it.

Figure 5-1. An iterator yielding to its invoking ...

Get The Ruby Programming Language now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.