An iterator is a looping construct in Ruby. It uses method syntax. We optionally use an iteration variable, enclosed in vertical bars.
We invoke times, upto, downto, step and each. With these iterators, we simplify loops—this eliminates bugs caused by complex logic.
We use the times iterator. Iterators can have an iteration variable, but they do not always need one. We can omit the iteration variable for simpler syntax.
times()
to repeat 4 times.# Part 1: use a times-loop. 4.times do puts "TIMES: 4" end # Part 2: use a times iterator with an iteration variable. 5.times do |i| puts "ITERATION: %d" % i endTIMES: 4 TIMES: 4 TIMES: 4 TIMES: 4 ITERATION: 0 ITERATION: 1 ITERATION: 2 ITERATION: 3 ITERATION: 4
This starts at the first number and proceeds through the argument. Here we use iteration variables "x" and "y." We also nest an iterator loop.
# Go up from 3 to 5. 3.upto(5) do |x| # Go up from x to x + 2. x.upto(x + 2) do |y| # Display variable. print y, " " end # End the line. print "\n" end3 4 5 4 5 6 5 6 7
short
syntaxIterators can be used with a short
block syntax form. We use curly brackets and omit the do keyword. This is shorter to type, but harder to add statements to later.
# Use block syntax. # ... The do keyword is not required here. # ... Use format string with puts to write the numbers. 0.upto(3) {|x| puts("Number: %x" % x)}Number: 0 Number: 1 Number: 2 Number: 3
This decrements a number. It reduces the number by 1 after each pass through the iterator body. If the argument is not lower, the iterator body is not executed.
# Decrement from 5 to 3. 5.downto(3) do |j| # Display the index. puts j end5 4 3
This allows us to specify all parts of a loop. We indicate the starting index, the ending index, and the step—the change after each iteration.
# Increment from 0 to 10, by 2 each time. # ... Name the iteration variable "v". 0.step(10, 2) do |v| puts v end # Decrement from 12 to 6, by -2 each time. # ... Name the iteration variable "iter". 12.step(6, -2) do |iter| puts iter end0 2 4 6 8 10 12 10 8 6
Should we always use step? The step method can duplicate the other loops shown. It is powerful, but may be more complex than upto or downto.
# These 2 iterators do the same thing. 0.upto(4) {|x| puts "UPTO: %d" % x} 0.step(4, 1) {|x| puts "STEP: %d" % x}UPTO: 0 UPTO: 1 UPTO: 2 UPTO: 3 UPTO: 4 STEP: 0 STEP: 1 STEP: 2 STEP: 3 STEP: 4
This is available on arrays. It accesses each array element. With the iteration variable, we can then test and use the element. The index number remains unavailable.
string
iterator, we have several ways to loop over the contents of strings.values = [10, 20, 30] # Use each on the array of integers. # ... Print each value with puts in a single-line block. values.each {|number| puts "VALUE: %d" % number}VALUE: 10 VALUE: 20 VALUE: 30
Here we can implement our own iterators. This code introduces an addthree()
method, which yields a sequence of numbers starting at zero, incrementing by 3 each time.
def addthree(max) # Return a sequence incremented by three up to the max. i = 0 while i <= max yield i i += 3 end end # Display sequence up to 20. addthree(20) do |n| puts n end0 3 6 9 12 15 18
Are iterators slower than for
-loops? If a certain form of iteration is faster, it can help many programs. Most programs spend most of their time in looping constructs.
for
-loop with a start index and a max index. It iterates the same number of times as version 1.count = 750000 n1 = Time.now.usec # Version 1: use times iterator. v = 0 count.times do v += 1 end puts v n2 = Time.now.usec # Version 2: use for-loop. v = 0 for i in 0..count-1 v += 1 end puts v n3 = Time.now.usec # Compute millisecond timings. puts ((n2 - n1) / 1000) puts ((n3 - n2) / 1000)750000 750000 61 [Time in ms: times() iterator] 64 [Time in ms: for-loop]
Ruby iterators help make loops simpler. With them, we eliminate index variables when possible. This reduces code complexity, which in turn prevents bugs.