Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enumerator as an infinite generator in Ruby

Tags:

I'm reading one resource explaining how Enumerators can be used as generators, which as an example like:

triangular_numbers = Enumerator.new do |yielder|   number = 0   count = 1   loop do     number += count     count += 1     yielder.yield number   end end  print triangular_numbers.next, " "  print triangular_numbers.next, " "  print triangular_numbers.next, " "  

I don't understand the purpose of yielder here, what value it takes, and how this code is executing in parallel with the rest of the program's code.

The execution starts at the top, and pauses probably when the block "yields" a value to my code.

Can someone please explain how all this executes in the compiler's eyes?

like image 338
daremkd Avatar asked Oct 29 '13 13:10

daremkd


People also ask

What is enumerator in Ruby?

Enumerator, specifically, is a class in Ruby that allows both types of iterations – external and internal. Internal iteration refers to the form of iteration which is controlled by the class in question, while external iteration means that the environment or the client controls the way iteration is performed.

What does the lazy method do to enumerators?

Enumerator::Lazy. Enumerator::Lazy is a special type of Enumerator , that allows constructing chains of operations without evaluating them immediately, and evaluating values on as-needed basis.


1 Answers

I think I've found something that you may find interesting.

This article: 'Ruby 2.0 Works Hard So You Can Be Lazy' by Pat Shaughnessy explains the ideas behind Eager and Lazy evaluation, and also explains how that relates to the "framework classes" like Enumerale, Generator or Yielder. It is mostly focused on explaining how to achieve LazyEvaluation, but still, it's quite detailed.


Original Source: 'Ruby 2.0 Works Hard So You Can Be Lazy' by Pat Shaughnessy

Ruby 2.0 implements lazy evaluation using an object called Enumerator::Lazy. What makes this special is that it plays both roles! It is an enumerator, and also contains a series of Enumerable methods. It calls each to obtain data from an enumeration source, and it yields data to the rest of an enumeration. Since Enumerator::Lazy plays both roles, you can chain them up together to produce a single enumeration.

This is the key to lazy evaluation in Ruby. Each value from the data source is yielded to my block, and then the result is immediately passed along down the enumeration chain. This enumeration is not eager – the Enumerator::Lazy#collect method does not collect the values into an array. Instead, each value is passed one at a time along the chain of Enumerator::Lazy objects, via repeated yields. If I had chained together a series of calls to collect or other Enumerator::Lazy methods, each value would be passed along the chain from one of my blocks to the next, one at a time

Enumerable#first both starts the iteration by calling each on the lazy enumerators, and ends the iteration by raising an exception when it has enough values.

At the end of the day, this is the key idea behind lazy evaluation: the function or method at the end of a calculation chain starts the execution process, and the program’s flow works backwards through the chain of function calls until it obtains just the data inputs it needs. Ruby achieves this using a chain of Enumerator::Lazy objects.

like image 174
quetzalcoatl Avatar answered Sep 18 '22 15:09

quetzalcoatl