问题
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