Table of contents
1.
Introduction
2.
Enumerators
3.
External Iterators
4.
Example
4.1.
Output
5.
Internal vs External Iterators in Ruby
6.
Frequently Asked Questions
6.1.
What exactly is the distinction between internal and external iterators in ruby?
6.2.
In Ruby, how do we use iterator?
6.3.
What is an external iterator?
6.4.
What is an enumerator in Ruby?
6.5.
Internally, how does iterator work?
7.
Conclusion
Last Updated: Mar 27, 2024
Easy

External Iterators in Ruby

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Iterator is a term used in object-oriented programming. Iteration is the repetition of a single action, similar to a loop. The loop method is the most basic iterator. They return all the components of a collection one by one. Arrays and hashes are examples of collections.

Although while, until, and for loops are fundamental to the Ruby programming language, iterators are arguably more commonly used to create loops.

For example:

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 }
You can also try this code with Online Ruby Compiler
Run Code

 

See the official documentation of ruby here.

Enumerators

An enumerator is an Enumerable object that enumerates another item. To utilise enumerators with Ruby 1.8, you must use the keyword 'enumerator'. Enumerators are built-in in Ruby 1.9. Therefore no need is required.

Enumerators are members of the Enumerable::Enumerator class. Although this class may be immediately constructed using new, this is not how most enumerators are built. Instead, use to enum or its synonym enum for, which are Object methods. With no parameters, to enum provides an enumerator whose each method calls the target object's each method.

We can send the arguments to the enum. However, the enum for synonym seems more logical in this instance. The first parameter must be a symbol representing an iterator method. The designated method of the original object will be invoked by each method of the resultant Enumerator. 

Any leftover enum for arguments will be given to the selected method. The String class in Ruby 1.9 is not Enumerable but has three iterator methods: char, byte, and line. Assume we wish to utilise an Enumerable function like the map based on each char iterator. We accomplish this by developing an enumerator:

For example:

s = "hello"

s.enum_for(:each_char).map {|c| c.succ } # => ["i", "f", "m", "m", "p"]
You can also try this code with Online Ruby Compiler
Run Code

External Iterators

Enumerators have been discussed in terms of their utility as Enumerable proxy objects. Enumerators are another essential purpose in Ruby 1.9: external iterators. You may use an enumerator to loop through the items of a collection by using the next function repeatedly. This function throws a StopIteration exception when there are no more elements.

For example:

iterator = 9.downto(1) # An enumerator as external iterator

begin 

 print iterator.next while true 

rescue StopIteration

 puts "...blastoff!" # An expected, nonexceptional condition

end
You can also try this code with Online Ruby Compiler
Run Code


External iterators in ruby are easy to use: simply use next each time you wish to add another element. When no more elements are found, the “next” throws a StopIteration exception. This may appear unusual—an exception is raised for a predicted termination condition rather than an unexpected and extraordinary event. In this external iteration mechanism, Ruby follows Python. By treating loop termination as an exception, you simplify your looping logic; there is no need to verify the next return value for a particular end-of-iteration value, and there is no need to execute any form of next function.

To make looping with external iterators in ruby easier, the “Kernel.loop” function provides an implicit rescue clause (in Ruby 1.9) and quits when StopIteration is invoked. As a result, the earlier countdown code might be more readily expressed as follows:

iterator = 9.downto(1)

loop do # Loop until StopIteration is raised

 print iterator.next # Print the next item

end

puts "...blastoff!"
You can also try this code with Online Ruby Compiler
Run Code

 

Another option is to give an external iterator to an internal iterator method, such as this one:

def iterate(iterator)

 loop { yield iterator.next }

end

iterate(9.downto(1)) {|x| print x }
You can also try this code with Online Ruby Compiler
Run Code

Example

The example below shows the parallel iteration with external iterators.

def sequence(*enumerable, &block)

 enumerable.each do |enumerable|

 enumerable.each(&block)

 end

end

# Iterate the provided collections, interleaving their elements.
# This cannot be completed efficiently without the need of external 
# iterators.

def interleave(*enumerable)

 # Convert enumerable collections to an array of enumerator.

 enumerator = enumerable.map {|e| e.to_enum }

 # Loop until we don't have any more enumerator.

 until enumerator.empty?

 begin

 e = enumerator.shift 

 yield e.next 

 rescue StopIteration 

 else # If no exception occurred

 enumerator << e 

 end

 end

end

# Iterate the provided collections, yielding tuples of values,
# one value from each of the collections. See also Enumerable.zip.

def bundle(*enumerable)

 enumerator = enumerable.map {|e| e.to_enum }

 loop { yield enumerator.map {|e| e.next} }

end

# Examples 

a,b,c = [4,5,6], 7..9, 'a'..'e'

sequence(a,b,c) {|x| print x} 

print "\n"

interleave(a,b,c) {|x| print x} 

print "\n"

bundle(a,b,c) {|x| print x}
You can also try this code with Online Ruby Compiler
Run Code

Output

external iterators in ruby example

Internal vs External Iterators in Ruby

A fundamental difficulty is determining who controls the iteration: the iterator or the client who utilises it. The iterator is referred to as an external iterator when the client controls the iteration. When the iterator controls the iteration, the iterator is referred to as an internal iterator. Clients who utilise an external iterator must progress the traverse and explicitly request the next element from the iterator. In contrast, the client assigns an operation to an internal iterator, and the iterator applies that action to each element.

Internal iterators are less adaptable than external iterators in ruby. It's simple to compare two collections for equality using an external iterator, but it's nearly complicated with internal iterators. On the other hand, internal iterators are simpler to use since they specify the iteration logic for us.

Internal iterators in Ruby are iterator methods like each; they control iteration and"push" values to the method call's related code block. Enumerators feature an "each" method for internal iteration. Still, they also operate as external iterators in Ruby 1.9 and later—client code may use next to successively "pull" data from an enumerator.

Frequently Asked Questions

What exactly is the distinction between internal and external iterators in ruby?

Iterators are classified into two types: external iterators in ruby and internal iterators in ruby. An external iterator is active, whereas an internal iterator is inactive. When the iteration is controlled by the client (i.e. the programmer), the iterator is referred to as an external iterator. It is referred to as an internal iterator when the iterator controls it.

In Ruby, how do we use iterator?

Ruby's object-oriented idea is "iterators." In a nutshell, Iterators are the techniques supported by collections (Arrays, Hashes etc.). Collections are objects that hold a group of data members. Ruby iterators return each element of a collection one by one.

What is an external iterator?

This Iterator is sometimes referred to as an active iterator or an explicit iterator. The programmer controls the iteration of components in this form of the iterator. The programmer determines when and how the next iteration element is invoked.

What is an enumerator in Ruby?

Enumerator, in particular, is a class that supports both internal and external iterators in ruby. Internal iteration refers to iteration managed by the class in question, whereas external iteration refers to iteration controlled by the environment or the client.

Internally, how does iterator work?

For most basic java collections, the iterator just stores a reference to where the iterator is in the collection. The iterator is advanced by using. next(). It does not duplicate the items and just returns the next entry in the collection.

See the official faqs of ruby here

Conclusion

In this article, we have extensively discussed the external iterators in ruby and the difference between internal and external iterators in ruby.

After reading about the external iterators in ruby and the difference between internal and external iterators in ruby, are you not feeling excited to read/explore more articles on the topic of DSA? Don't worry; Coding Ninjas has you covered. To learn, see Try Ruby, the History of Ruby and Constants in Ruby.

Refer to our Guided Path on Coding Ninjas Studio to upskill yourself in Data Structures and AlgorithmsCompetitive ProgrammingJavaScriptSystem Design, and many more! If you want to test your competency in coding, you may check out the mock test series and participate in the contests hosted on Coding Ninjas Studio! But if you have just started your learning process and are looking for questions asked by tech giants like Amazon, Microsoft, Uber, etc., you must look at the problemsinterview experiences, and interview bundle for placement preparations.

Nevertheless, you may consider our paid courses to give your career an edge over others!

Do upvote our blogs if you find them helpful and engaging!

Happy Learning!

Live masterclass