I am trying to familiarize with itertools. As an exercise, I am trying to generate (x,y) integer pairs satisfying -1 < y < x <5. The following code using itertools produces incorrect results:
from itertools import *
xs = xrange(0,5)
allPairs = chain(*(izip(repeat(x), takewhile(lambda y: y < x, count())) for x in xs))
print list(allPairs)
The output is
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3), (4, 0), (4, 1), (4, 2), (4, 3)]
The problem seems to be that the lambda function within the takewhile is using x = 4, the maximum in the range.
If I modify the code above to explicitly construct a lambda function for each x as follows the output is computed correctly.
from itertools import *
xs = xrange(0,5)
xypair = lambda x: izip(repeat(x), takewhile(lambda y: y < x, count()))
allPairs = chain(*(xypair(x) for x in xs))
print list(allPairs)
Output:
[(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3)]
Any idea why in the first code the lambda is constructed only using the last value of the iteration over xs?