lazy evaluation is sometimes too lazy... help please.

James Stroud jstroud at mbi.ucla.edu
Fri Jan 16 10:37:47 CET 2009

```Ken Pu wrote:
> Hi,  below is the code I thought should create two generates, it[0] =
> 0,1,2,3,4,5, and it[1] = 0,10,20,30,..., but they turn out to be the
> same!!!
>
> from itertools import *
> itlist = [0,0]
> for i in range(2):
>   itlist[i] = (x+(i*10) for x in count())
>
> print "what's in the bags:"
> print list(islice(itlist[0], 5))
> print list(islice(itlist[1], 5))
>
> The output is:
> [10, 11, 12, 13, 14]
> [10, 11, 12, 13, 14]
>
> I see what Python is doing -- lazy evaluation doesn't evaluate
> (x+(i*10) for x in count()) until the end.

It doesn't evaluate it until you ask it to, which is the right behavior.
However, when evaluated, it evaluates "i" also, which is the last value
to which "i" was assigned, namely the integer 1. I'm going to get flamed
pretty hard for this, but it doesn't seem to be the intuitive behavior
to me either. However, in a purely functional language, you wouldn't be
able to construct a list of generators in this way.

With python, you have to remember to adopt a purely functional design
and then pray for best results. You can store generators in a list, but
they need to be constructed properly. I can't perfectly transmogrify
your code into functional code because I don't think making the
particular anonymous generator you want is possible in python. However
this is how I would make a close approximation:

from itertools import *

def make_gen(i):
for x in count():
yield x + (i * 10)

itlist = [make_gen(i) for i in xrange(2)]

print "what's in the bags:"
print list(islice(itlist[0], 5))
print list(islice(itlist[1], 5))

James

--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com

```