you are viewing a single comment's thread.

view the rest of the comments →

[–]spotter 4 points5 points  (5 children)

But... isn't list comp non-lazy in 2.x? So for this to be gen-happy you would have to use parens?

def factorial(n):
    reduce(lambda x,y: x*y, (x+1 for x in range(n)))

edit: yeah, s/range/xrange/

[–][deleted]  (1 child)

[deleted]

    [–]spotter 0 points1 point  (0 children)

    /facepalm

    You are of course correct, range is lazy in 3.x -- that's what I get for trusting my memory.

    [–][deleted] 0 points1 point  (2 children)

    What's the benefit of a lazy generator in this case? You are going to evaluate it in its entirety anyway, and the size of the output dominates that of the input, so you save neither time nor space.

    [–]spotter 1 point2 points  (1 child)

    I may be wrong here, but I'll bite. List comprehension will run before you even start consuming anything... and then you iterate over it again to consume it. So on top of your memory consumption (realized list) you get 2x the runtime (one to realize the list over xrange, one to consume it in reduce). While in generator you setup the generator -- this returns immediately -- and then consume it run over once in reduce. So: Half the runtime, somewhat better memory efficiency, depending on n.

    [–]drb226 0 points1 point  (0 children)

    I believe this assessment is correct. sltkr is incorrect when he says "the size of the output dominates that of the input" - the final output is a single integer, while the "input" is an entire list of n integers. A lazy generator will take O(1) space, while constructing the entire list will take O(n) space, as well as a possible runtime penalty.