Lazy Evaluation in Python

Posted on April 05, 2019 in Programming

Lazy evaluation

Say, I wrote a program that generates fibonacci numbers. I could write a loop keeping in memory last two fibonacci numbers and returning the new one as the sum of last two. We've all seen this.

last = 1
secondlast = 1
while (True):
    next = last + secondlast
    secondlast = last
    last = next
    print(next)

This gives a 'infinite' list of fibonacci numbers. But the problem is this is essentially useless as it is. I'd like it to stop after the evaluation of each fibonacci number so that I can do some other complicated thing elsewhere.
The answer to this is lazy evaluation. Generating the next number only when we need it and yet maintaining this kind of loop structure.

Prime Generator

Let's use the definition of prime. A number is prime if it has only \(1\) and \(n\) itself as a factors. To that extent, we first define a function that returns factors:

def fact(n):
    return [x for x in range(1, n+1) if n%x == 0]

And, we can define prime as:

def prime(n):
    return fact(n) == [1,n]

With this definition, as you can see, prime(2) should return True and prime(14) should return False. Now, we can use lazy evaluation to check all the integers \(n \in Z ^+\) for primality as the following:

def nextprime():
    i = 1
    while (True):
        i = i + 1
        if (prime(i)):
            yield i

Just for sanity check, copy and paste the above definitions in a python3 console and do the following:

>>> a = nextprime()
>>> next(a)
2
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7

and so on. This is exactly what we were looking for. Generate the next prime only when we need it.

Twin primes

Let's go a little further and generate twin primes. First, let's define what twin numbers are:

def twin(n, m):
    return abs(n-m)==2

Sure enough, twin(2,3) returns False and twin(3,5) returns True.

Next, we use this definition to filter out twin primes:

def listTwinPrimes():
    a = nextprime()
    b = nextprime()
    next(b)
    for i,j in zip(a,b):
        if twin(i,j):
            print(i, j)

Admittedly, this performs terribly. Prime generation algorithm can do much better, and also every prime is generated twice: once from a and b each. But it serves the purpose of illustration and gives you an 'infinite' list of twin primes. ;)

The End