2009-02-16 7 views
5

J'essaye de générer le morris sequence en python. Ma solution actuelle est ci-dessous, mais j'ai l'impression d'avoir écrit c en python. Quelqu'un peut-il fournir une solution plus pythonique?Quelqu'un peut-il fournir une façon plus pythonique de générer la séquence Morris?

def morris(x): 
    a = ['1', '11'] 
    yield a[0] 
    yield a[1] 
    while len(a) <= x: 
     s = '' 
     count = 1 
     al = a[-1] 
     for i in range(0,len(al)): 
      if i+1 < len(al) and al[i] == al[i+1]: 
       count += 1 
      else: 
       s += '%s%s' % (count, al[i]) 
       count = 1 
     a.append(s) 
     yield s 
a = [i for i in morris(30)] 
+0

Il semble que yo Je vous pose des questions sur la danse des claquettes de poisson. Cela combine Monty Python et Morris Dancing ... :-) –

+0

Si cette chose ne nécessitait pas au moins 10 caractères j'aurais juste répondu avec: D –

+0

pourquoi êtes-vous à la fois yield() et stocker des valeurs sur un [] ? – Javier

Répondre

23

itertools.groupby semble parfaitement s'adapter! Il suffit de définir une fonction next_morris comme suit:

def next_morris(number): 
    return ''.join('%s%s' % (len(list(group)), digit) 
        for digit, group in itertools.groupby(str(number))) 

C'est tout !!! Regardez:

print next_morris(1) 
11 
print next_morris(111221) 
312211 

Je pourrais l'utiliser pour faire un générateur:

def morris_generator(maxlen, start=1): 
    num = str(start) 
    while len(num) < maxlen: 
     yield int(num) 
     num = next_morris(num) 

Utilisation:

for n in morris_generator(10): 
    print n 

résultats:

1 
11 
21 
1211 
111221 
312211 
13112221 
+0

merci beaucoup. J'ai vraiment, vraiment besoin d'apprendre la bibliothèque iterools mieux –

6
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)) 

tout d'abord je voudrais faire l'itérateur infini et laisser le consommateur décider, combien il qu'il veut. De cette façon, il peut soit obtenir tous les nombres de morris qui sont plus courts que x ou les premiers nombres x, etc.

Ensuite, il n'est évidemment pas nécessaire de stocker la liste entière des nombres morris précédents dans une liste, puisque la récursivité est seulement n := f(n-1) de toute façon.

Enfin, utiliser itertools pour lui donner une touche fonctionnelle vaut toujours un point geek ou deux;) Je divise l'expression du générateur en plusieurs lignes pour le rendre un peu plus facile pour l'œil.

La principale laideur de cette solution vient du fait que len() ne peut pas être appelée sur un itérateur et nous donne un int où nous avons besoin d'un str. L'autre hickup est le str.join imbriqué) pour aplatir le tout en str.

Si vous voulez commencer la séquence de nombres arbitraires, définir la fonction comme ceci:

def morris(morris=None): 
    if morris is None: 
     morris = '1' 
[...] 

Si vous voulez tourner autour de ce générateur, vous pouvez l'écrire comme ceci:

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 

Je ne suis pas sûr que j'aime le fractionnement en deux fonctions, mais cela semble être la solution la plus lisible:

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 

J'espère que vous l'aimez!

Questions connexes