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

Discussion in 'Python' started by Ken Pu, Jan 16, 2009.

  1. Ken Pu

    Ken Pu Guest

    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 = (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
    Ken Pu, Jan 16, 2009
    #1
    1. Advertising

  2. 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 = (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]
    Michael Hartl, Jan 16, 2009
    #2
    1. Advertising

  3. Ken Pu

    alex23 Guest

    James Stroud <> 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.
    alex23, Jan 16, 2009
    #3
  4. 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 = ( 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 = gen(i)



    --
    Steven
    Steven D'Aprano, Jan 16, 2009
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Saunders
    Replies:
    0
    Views:
    444
    John Saunders
    Aug 28, 2003
  2. Nehmo Sergheyev
    Replies:
    1
    Views:
    489
    Andrew Urquhart
    May 9, 2004
  3. Marcin Vorbrodt

    ::std sometimes needed, sometimes not

    Marcin Vorbrodt, Sep 16, 2003, in forum: C++
    Replies:
    24
    Views:
    741
    Jerry Coffin
    Sep 17, 2003
  4. Boris Borcic
    Replies:
    0
    Views:
    535
    Boris Borcic
    Jan 16, 2009
  5. Boris Borcic
    Replies:
    0
    Views:
    536
    Boris Borcic
    Jan 16, 2009
Loading...

Share This Page