How does Ruby enumerator terminate iteration?

匆匆过客 提交于 2021-01-27 06:28:44

问题


Friends, please I need help with this explanation: In the Ruby code below, what condition termites the loop do? It's supposed to be an infinite loop, but, how does it terminate?

# Ruby code
fib = Enumerator.new do |y|
  a = b = 1
  loop do
    y << a
    a, b = b, a + b
  end
end

p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Your contributions will be highly appreciated.


回答1:


(Source: https://rossta.net/blog/infinite-sequences-in-ruby.html)

The way you have implemented the function fib allows it to be "lazy." What this means, is that Ruby will not try to calculate the values in fib until it absolutely has to.

The take method on the last line is key here.

p fib.take(10) 

Basically, Ruby says "I'm going to evaluate the first 10 values of fib and pretend the rest do not exist, as I don't have to use them yet."

In other words, while it is true that the fib function is potentially infinite, you only asked for the first 10 values, so you only got the first 10 values.

If you tried something like this:

p fib.to_a

Your program would get stuck. Why? Because the to_a (to array) method wants to try get all the values of fib, not just a few of them. Obviously, you cannot get all the values of an infinite list.

For more information:

https://medium.com/background-thread/what-is-lazy-evaluation-programming-word-of-the-day-8a6f4410053f

https://github.com/juinc/tilap/issues/9

--- EDIT: ---

Technical correction: As Cary Swoveland pointed out, it would be more technically correct to say that fib is an algorithm/machine that produces values on demand.




回答2:


An enumerator is like a Pez® dispenser, which ejects a peppermint candy each time the top of the dispenser is pressed. An enumerator version of the dispenser would not hold a supply of candies, but would manufacture the candies one at a time, on demand, possibly being capable of producing an infinite number of them.

One type of an enumerator is tied to an underlying collection of objects. Here are two that are tied to an array.

enum = [1,2,3].each #=> #<Enumerator: [1, 2, 3]:each> 

enum.next #=> 1 
enum.next #=> 2 
enum.next #=> 3 
enum.next #=> StopIteration (iteration reached an end) 

enum = [1,2,3].cycle #=> #<Enumerator: [1, 2, 3]:cycle> 

enum.next #=> 1 
enum.next #=> 2 
enum.next #=> 3 
enum.next #=> 1 
enum.next #=> 2 
... ad infinitum

enum.first(8)
  #=> [1, 2, 3, 1, 2, 3, 1, 2] 

In the first example only a finite number of objects are generated by the enumerator before a StopIteration exception is raised. In the second example an arbitrary number of objects can be generated, but only on demand. first, for example, instructs enum 8 times to generate and pass to itself one object. enum is not lazy; it is all-to-eager to comply, but will not manufacture and dispense an object until it is instructed to do so.

The other type of enumerator generates objects according to a set of rules that it was born with, rules that are not tied to an underlying object. Those enumerators are generally capable of generating an infinite number of objects. The enumerator that generates Fibonacci numbers is an example of that type of enumerator. It is not a loop that does not terminate; it is a machine that is capable of producing any number of objects, but only one at a time, on demand.



来源:https://stackoverflow.com/questions/61872291/how-does-ruby-enumerator-terminate-iteration

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!