What Xanthir said, although you really only need to worry about

m. Here's my version. It creates the generator expressions using a function

multiples that takes

i and

m as args. It

would work if you only pass

m as an arg, but I think it improves readability to pass them both.

My code doesn't use

yield from because I'm running an ancient version of Python. This code should perform correctly on Python 2 or 3, although on Python 2 it'd be slightly better to replace

range with

xrange and

zip with

izip.

Code: Select all

from itertools import tee, chain, groupby, islice

from heapq import merge

def hamming0():

def deferred_output():

for u in output: yield u

p2, p3, p5, result = tee(deferred_output(), 4)

m2, m3, m5 = (2*x for x in p2), (3*x for x in p3), (5*x for x in p5)

output = (k for k,_ in groupby(chain([1], merge(m2, m3, m5))))

return result

def hamming1(ps=(2,3,5)):

def deferred_output():

for u in output: yield u

t = tee(deferred_output(), len(ps)+1)

def multiples(i, m):

return (m*x for x in t[i])

ms = [multiples(i, m) for i,m in enumerate(ps)]

output = (k for k,_ in groupby(chain([1], merge(*ms))))

return t[-1]

print(all(u == v for _,u,v in zip(range(999), hamming0(), hamming1())))

print(list(islice(hamming1((3,5,7)), 20)))

Output

Code: Select all

`True`

[1, 3, 5, 7, 9, 15, 21, 25, 27, 35, 45, 49, 63, 75, 81, 105, 125, 135, 147, 175]

FWIW, you could implement

multiples as a lambda,

Code: Select all

`ms = [(lambda m=m: (m*x for x in t[i]))() for i,m in enumerate(ps)]`

but that's far less readable, IMHO. Also, it's a little less efficient since the lambda itself is re-defined on every iteration of the enumeration.

Actually, it's probably more Pythonic to implement

multiples as a simple generator function:

Code: Select all

def hamming1(ps=(2,3,5)):

def deferred_output():

for u in output: yield u

t = tee(deferred_output(), len(ps)+1)

def multiples(i, m):

for x in t[i]:

yield m * x

ms = [multiples(i, m) for i,m in enumerate(ps)]

output = (k for k,_ in groupby(chain([1], merge(*ms))))

return t[-1]