illegal-immigrant
illegal-immigrant

Reputation: 8224

Infinite yield problem

Here is my simple code

class Fibonacci:
    @staticmethod
    def series():
        fprev = 1
        fnext = 1
        yield fnext
        while True:
            yield fnext
            fprev,fnext = fnext,fprev+fnext

under10 = (i for i in Fibonacci.series() if i<10)
for i in under10 :
    print i

It's absolutely obvious, but...WHY interpreter is executing block

while True:
                yield fnext
                fprev,fnext = fnext,fprev+fnext

Forever? I specified in generator,that I want only elements<10

under10 = (i for i in Fibonacci.series() if i<10)

IMHO, it's a little bit misunderstanding Any way to prevent infinite execution without re-writing "series"?

Upvotes: 5

Views: 2118

Answers (3)

martineau
martineau

Reputation: 123393

The infinite loop isn't a result of the while True: in the Fibonacci.series() method. It's caused by the under10 = (i for i in Fibonacci.series() if i<10) generator which just keeps going since it doesn't realize the values yielded will never get smaller. Here's [another] way to fix it and generalize it at the same time -- without re-writing series() -- using the itertools.takewhile() iterator:

import itertools
fibos_under = lambda N: itertools.takewhile(lambda f: f < N, Fibonacci.series())

for i in fibos_under(10):
    print i

BTW: You can simplify the Fibonacci.series() method slightly by changing it to this which yields the same values:

class Fibonacci:
    @staticmethod
    def series():
        fprev,fnext = 0,1
        while True:
            yield fnext
            fprev,fnext = fnext,fprev+fnext

Upvotes: 0

Konrad Rudolph
Konrad Rudolph

Reputation: 545508

How should the interpreter know that all future numbers will be < 10? It would have to either know (somehow) that it’s churning out the Fibonacci series, or it would have to inspect the whole series.

It can’t do the first, so it does the second.

You can fix this by using itertools.takewhile:

import itertools

under10 = itertools.takewhile(lambda n: n < 10, Fibonacci.series())

Upvotes: 16

GWW
GWW

Reputation: 44093

under10 = (i for i in Fibonacci.series() if i<10) 

Will keep going, it just won't yield values greater than 10. There's nothing instructing the for loop to stop.

You would probably have better luck doing something like:

for i in Fibonacci.series():
    if i > 10:
        break
    #do your appends and such here

EDIT:

I like Konrad's itertools example much more, I always forget about itertools

Upvotes: 1

Related Questions