2013-04-03 10 views
5

Sto provando a raggruppare le carte dello stesso seme (colore) e classificarle all'interno dei generatori e immagazzinare quei generatori all'interno di una lista di comprensione.creazione di più generatori all'interno di una lista comprensione

La soluzione che ho trovato è quella fatta eccezione per il fatto che tutti i generatori contengono esattamente le stesse carte. Qualche idea del perché?

Ecco il codice

deck=range(52) 

gens=[(i for i in deck if i%13==v) for v in range(13)] 

Sulla base di questo mi aspetterei per esempio:

gens[1].next() 
1 
gens[1].next() 
14 


gens[10].next() 
10 
gens[10].next() 
23 

Ma invece ottengo

gens[1].next() 
12 

gens[1].next() 
25 

gens[1].next() 
38 

E tutti i generatori di ritorno lista gli stessi risultati ..

risposta

7

Il problema è che il nome v nell'espressione del generatore si riferisce a tale variabile v nella comprensione della lista. Quindi, quando il codice dell'espressione del generatore viene effettivamente eseguito (quando si chiama next), viene visualizzata la variabile v e viene visualizzato il valore 12, indipendentemente dal valore di v durante la creazione del generatore.

Una soluzione:

deck = range(52) 

def select_kth(v): 
    return (i for i in deck if i % 13 == v) 

gens = [select_kth(v) for v in range(13)] 

Perché abbiamo definito una funzione, il nome v arriva a vivere nel proprio ambiente di denominazione e così rimane intorno non modificato.

Se si voleva davvero, si potrebbe fare questo in una sola riga:

gens = [(lambda v: (i for i in deck if i % 13 == v))(v) for v in range(13)] 
+0

Grazie. Questa risposta e gli @abarnert sono entrambi eccellenti – jule64

5

Se si attiva questa nei cicli annidati equivalenti, si può vedere il problema scoping più facilmente:

gens = [] 
for v in range(13): 
    def gen(): 
     for i in deck: 
      if i%13 == v: 
       yield i 
    gens.append(gen()) 

Si finisce con 13 generatori tutti legati allo stesso valore di v, 12.

Quindi, la soluzione qui è la stessa di qualsiasi altro problema di ambito: è necessario creare un nuovo ambito con il v in esso. Il modo più semplice per farlo è quello di creare una nuova funzione:

gens = [(lambda x: (i for i in deck if i%13==x)(v) for v in range(13)] 
+0

Stavo per cancellarlo, perché è la stessa risposta di Dougal's, e lui è arrivato qui un minuto prima di me, ma ... ha delle revisioni, quindi credo che la gente pensi che valga la pena tenersi in giro comunque ... O forse Dougal dovrebbe semplicemente copiare la conversione in cicli annidati e avere il meglio di entrambi i mondi in una sola risposta? – abarnert

+0

La tua risposta è un modo totalmente diverso di spiegarlo. Lo lascerei. – William

+0

Mi piace la tua spiegazione e penso che valga la pena tenerla da sola piuttosto che rubarla, non c'è bisogno di cancellarla. :) – Dougal

Problemi correlati