Iterators in Ruby
In Ruby programming, iterators are a fundamental idea that are commonly used in place of more traditional loop statements like while and for. An iterator in Ruby is a unique type of method that mostly functions as a loop and works closely with a block of code that comes after it.
The Core Mechanism: and Blocks
Iterators are made possible by the yield
statement.
Control Flow: Control is momentarily returned to the block of code linked to the method invocation from the iterator method by the yield
statement.
Looping: The yield
statement is usually called several times by an iterator method in order to implement a looping structure.
Returning Control: Execution restarts at the statement just after yield
, and the iterator method automatically regains control when the block is finished.
Passing Values: The yield
statement can be followed by expressions to pass values from the iterator to the block. These values are allocated to parameters that are indicated by vertical bars (|…|
) and defined at the beginning of the block.
The keywords do and end or curly brackets ({}
) can be used to delimit a section of code. For simple, single-line blocks, curly brackets are utilised, whereas the do
…end
syntax is typically chosen for multi-line blocks.
You can also read What Is Mean By Operators In Ruby, It’s Types With Examples
Types of Iterators
Generally speaking, iterators are categorised according to the section of code that governs the iteration:
Internal Iterators (The Ruby Default)
The iterator method itself rules this style and “pushes” values to the block that is attached. It includes the majority of conventional Ruby iterators.
Numeric Iterators
There are numerous frequently used iterators defined by the Integer
class:
Iterator Method | Description | Code Example | Output | Source |
times | Invokes the block $N$ times, passing values 0 through $N-1$. | 3.times { print "Ruby! " } | Ruby! Ruby! Ruby! | |
upto | Counts up from the receiver to the argument, including both endpoints. | `1.upto(3) { | x | print x }` |
downto | Iterates downwards from the receiver to the argument. | `5.downto(1) { | n | print n }` |
Collection Iterators (each
and Enumerable Methods)
Each, the most popular iterator, calls the corresponding block once for every element in the collection (such as an array, hash, or range).
The Enumerable
module is frequently included in classes that specify an each
method, and each of these classes gains over 20 iteration-related methods (such as collect, select, inject, and sort
) built upon it.
Iterator Method | Description | Code Example | Output/Result | Source |
each | Passes each element of the collection to the block. | `.each { | x | print x*2 }` |
collect (or map ) | Executes the block for each element and collects the block’s return values into a new array. | `.collect { | x | x*x}` |
select | Returns an array of elements for which the block returns a value that is not false or nil . | `(1..10).select { | x | x%2 == 0}` |
inject | Accumulates a value across iterations, returning the final accumulated value. | data = `data.inject { | sum, x | sum + x }` |
Writing a Custom Internal Iterator (using yield
)
You can use the yield
statement to define your own iterator methods. This approach frequently behaves like a custom loop since it anticipates a block and calls it again.
def sequence(n, m, c)
i = 0
while(i < n) # Loop n times
yield m*i + c # Invoke the block, and pass a value to it
i += 1 # Increment i each time
end
end
# Invocation:
sequence(3, 5, 1) {|y| puts y }
Output
1
6
11
A while loop is used internally in the implementation of this sequence method, and yield is called to pass calculated values to the corresponding block.
External Iterators (Enumerators)
Enumerating another object is the main function of an enumerator, which is an enumerable object. Because Enumerators are built into Ruby 1.9 and act as external iterators, client code can use the next function to sequentially “pull” values. In contrast, the method pushes values to the block in internal iterators.
An Enumerator object can be obtained by using methods such as to_enum or enum_for, or by executing an iterator method without providing a block.
Example of an External Iterator (Pulling values):
The next function is repeatedly used by the external iterator model until a StopIteration exception is raised, signifying the end of the sequence. This exception is implicitly rescued for simple looping by the Kernel.loop
method.
iterator = 9.downto(1) # An enumerator as external iterator
loop do # Loop until StopIteration is raised
print iterator.next # Call the next method repeatedly
end
puts "...blastoff!"
Output
987654321...blastoff!
The rewind
method can be used to resume external iterators (albeit usually not a continuous file stream, provided the underlying source permits it, such as a Range or Array).
Example of a Custom Enumerator (for infinite streams):
An unending stream of values can be defined with enumerators.
fibonacci = Enumerator.new do |yielder|
a, b = 0, 1
loop do
yielder << a
a, b = b, a + b
end
end
# This line now prints the output
puts fibonacci.take(10).inspect
Output
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
You can also read Conditional Statements In Ruby: A Control Flow Techniques