2010-12-14 14 views
5

Sto scrivendo un funzione di attraversamento albero DF ampiezza, e quello che voglio fare è questo: Python idioma: propagazione dei rendimenti o sequenze di appiattimento?

def traverse(node): 
    yield node 
    for n in node.children: 
     yield_all traverse(n) # << if Python had a yield_all statement 

L'idea è di finire con un (piatta) sequenza di nodi nell'albero .

Approccio # 1: (i rendimenti che si propagano)

def traverse(node): 
    yield node 
    for n in node.children: 
     for m in traverse(n): 
      yield m 

Approccio # 2: (sequenze appiattimento)

def traverse(node): 
    return itertools.chain([node],*(traverse(n) for n in node.children)) 

Il primo approccio sembra più pulito, ma mi sento strano esplicitamente yield ing ogni nodo nella sottostruttura di ogni livello.

Il secondo approccio è terso e un po 'sporca, ma corrisponde a ciò che avrei scritto in Haskell:

traverse node = node : concatMap traverse (children node) 

Quindi la mia domanda è: quale è meglio? O mi manca una migliore terza opzione?

+0

di lista renderebbe questo pulitore. –

+0

Rafe: Scrivi una risposta e mostrami! :-) – perimosocordiae

+1

Mi piacerebbe vedere una lista di comprensione per questo ... avresti bisogno di appiattirlo alla fine, giusto? Per quanto mi riguarda, la soluzione "chain" è meravigliosa. – delnan

risposta

4

[UPDATE] CfrPEP-380, questo rendimento tutto sintassi è disponibile a partire da Python 3.3 come yield from:

def traverse(node): 
    yield node 
    for n in node.children: 
     yield from traverse(n) 
+0

@ THC4k, questa è una bella domanda :-) Ho c & p il codice da un altro script, credo che stavo provando qualcosa al momento. Aggiornato, dovrebbe funzionare senza usare "lista".[oops, hai cancellato il tuo commento?] – tokland

+0

sì quando lo hai cambiato;) –

+0

@ THC4k, sì, mi dispiace, non sono mai soddisfatto delle mie risposte e continuo a modificarle ;-) – tokland

3

mi piacerebbe andare con il primo. Si supererà la propagazione dei rendimenti dopo un paio di volte. :-)

1

Questa è una domanda di opinioni, quindi tutte le risposte saranno solo giudizi di valore. Per quanto posso pensare, non c'è una terza via elegante, però.

La mia opinione è che il primo modo vince a mani basse. È più chiaro e più facile da leggere - Python non è Haskell, anche se può fare qualcosa di funzionale, e spesso l'approccio funzionale non è così semplice.

0

Avanzamento con posizione di nodo:

def iter_tree(t, i=0, j=0): 
    yield (i, j), t 
    for j, n in enumerate(t.children): 
     yield from iter_tree(n, i + 1, j) 

for (i, j), n in iter_tree(t): 
    print(i*' ', (i, j), n)