Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   lazy evaluation is sometimes too lazy... help please. (http://www.velocityreviews.com/forums/t665807-lazy-evaluation-is-sometimes-too-lazy-help-please.html)

 Ken Pu 01-16-2009 07:51 AM

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

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. But is this the right
behaviour? How can I get the output I want:
[0, 1, 2, 3, 4]
[10, 11, 12, 13, 14]

Thanks.

Ken

 Michael Hartl 01-16-2009 10:17 AM

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

James Stroud schrieb:
> 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
>

You could just as well use the original expression in make_gen, too:

from itertools import *
def make_gen(i):
return (x + (i*10) for x in count())

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))

what's in the bags:
[0, 1, 2, 3, 4]
[10, 11, 12, 13, 14]

 alex23 01-16-2009 10:45 AM

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

James Stroud <jstr...@mbi.ucla.edu> wrote:
> I'm going to get flamed
> pretty hard for this, but it doesn't seem to be the intuitive behavior
> to me either.

Given this is the second time this issue has come up today, I'd have
to agree with you.

 Steven D'Aprano 01-16-2009 11:23 AM

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

On Fri, 16 Jan 2009 02:51:43 -0500, 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!!!

[...]
> I see what Python is doing -- lazy evaluation doesn't evaluate (x+(i*10)
> for x in count()) until the end. But is this the right behaviour? How
> can I get the output I want: [0, 1, 2, 3, 4]
> [10, 11, 12, 13, 14]

The solution I would use is:

itlist = [0,0]
for i in range(2):
itlist[i] = ( lambda i: (x+(i*10) for x in count()) )(i)

Or pull the lambda out of the loop:

itlist = [0,0]
def gen(i):
return (x+(i*10) for x in count())

for i in range(2):
itlist[i] = gen(i)

--
Steven

 All times are GMT. The time now is 04:53 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.