2009-02-16 3 views
5

Sto provando a generare lo morris sequence in python. La mia attuale soluzione è sotto, ma mi sento come se avessi appena scritto c in python. Qualcuno può fornire una soluzione più pitoniosa?Qualcuno può fornire un modo più pitioso di generare la sequenza di 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

suona come yo stai chiedendo del pesce che schiaffeggia la danza. Che combina Monty Python e Morris Dancing ... :-) –

+0

Se questa cosa non richiedesse almeno 10 caratteri, avrei semplicemente risposto con: D –

+0

perché stai sia yield() che memorizzando valori su un [] ? – Javier

risposta

23

itertools.groupby sembra adattarsi perfettamente! Basta definire una funzione next_morris come segue:

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

Questo è tutto !!! Guardate:

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

potrei usare che per fare un generatore:

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

utilizzo:

for n in morris_generator(10): 
    print n 

risultati:

1 
11 
21 
1211 
111221 
312211 
13112221 
+0

grazie mille. ho davvero bisogno di imparare meglio la libreria iterools –

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

Prima di tutto mi piacerebbe fare l'iteratore infinito e lasciare che il consumatore a decidere, quanto di esso che vuole. In questo modo è possibile ottenere tutti i numeri morris più brevi di x oi primi x numeri, ecc.

Quindi non c'è ovviamente bisogno di memorizzare l'intera lista dei numeri di morris precedenti in una lista, poiché la ricorsione è solo n := f(n-1) comunque.

Infine, l'uso di itertools per dare un tocco funzionale vale sempre un punto o due geek;) Ho diviso l'espressione del generatore in più righe per renderlo un po 'più facile per gli occhi.

La bruttezza principale di questa soluzione deriva dal fatto che len() non può essere chiamato su un iteratore e ci fornisce un int dove abbiamo bisogno di uno str. L'altro hickup è lo str.join nidificato) per appiattire di nuovo l'intera cosa in una str.

Se si desidera avviare la sequenza di numeri arbitrari, definire la funzione come questa:

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

Se si vuole girare intorno che generatore, è possibile scrivere in questo modo:

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 

non sono sicuro che mi piace la scissione in due funzioni, ma questo sembra essere la soluzione più leggibile:

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 

Spero che ti piaccia!