from itertools import groupby, islice
def morris():
morris = '1'
yield morris
while True:
morris = groupby(morris)
morris = ((len(list(group)), key) for key, group in morris)
morris = ((str(l), k) for l, k in morris)
morris = ''.join(''.join(t) for t in morris)
yield morris
print list(islice(morris(), 10))
First of all I'd make the iterator infinite and let the consumer decide, how much of it he wants. That way he could either get every morris number that is shorter than x or the first x numbers, etc.
Then there is obviously no need to store the whole list of previous morris numbers in a list, since the recursion is only n := f(n-1)
anyway.
Lastly, using itertools to give it a functional touch is always worth a geek point or two ;) I split the generator expression into several lines to make it a bit easier on the eye.
The main ugliness in this solution comes from the fact that len()
can't be called on an iterator and gives us an int where we need a str. The other hickup is the nested str.join) to flatten the whole thing into a str again.
If you want to start the sequence from arbitrary numbers, define the function like this:
def morris(morris=None):
if morris is None:
morris = '1'
[...]
If you want to turn around that generator, you can write it like this:
def morris():
morris = '1'
yield morris
while True:
print morris
morris = ''.join(''.join(t)
for t in ((str(len(list(group))), key)
for key, group in groupby(morris)))
yield morris
I'm not sure i like the splitting into two functions, but this seems to be the most readable solution:
def m_groupby(s):
for key, group in groupby(s):
yield str(len(list(group)))
yield key
def morris():
morris = '1'
yield morris
while True:
morris = ''.join(m_groupby(morris))
yield morris
Hope you like it!