2014-08-30 1 views
5

Stavo cercando di fare qualcosa in Python che utilizza la seguente procedura generale e voglio sapere qual è il modo migliore per appropriarsene.La maggior parte del modo Pythonic per costruire iterativamente un elenco?

Innanzitutto, una fase di inizializzazione:

  • creare un elemento M.
  • Creare un elenco L e aggiungere M a L.

secondo cappio attraverso la seguente:

  • Creare un nuovo articolo modificando l'ultimo elemento aggiunto a L.
  • Aggiungere il nuovo elemento a L.

Come semplice esempio, dire che voglio per creare una lista di liste in cui la lista ennesima contiene i numeri da 1 a n. Potrei usare la seguente (sciocca) procedura.

  • Inizialmente M è [1] e L = [[1]].
  • Quindi, modifica [1] aggiungendone 2 per creare il nuovo elemento [1,2], quindi aggiungi [1,2] a L così L = [[1], [1,2]].
  • Quindi, modifica [1,2] aggiungendo 3 ad esso per creare il nuovo elemento [1,2,3], quindi aggiungi [1,2,3] a L così L = [[1], [1] , 2], [1,2,3]].
  • Quindi, modifica [1,2,3] aggiungendo 4 a esso per creare il nuovo elemento [1,2,3,4], quindi aggiungi [1,2,3,4] a L così L = [ [1], [1,2], [1,2,3], [1,2,3,4]]. ecc

ho provato un paio di cose, ma la maggior parte di loro di modificare non solo l'ultimo elemento aggiunto, ma anche oggetti aggiunto L nelle fasi precedenti. Per il particolare problema che mi interessava, sono riuscito a trovare una soluzione che si comporta correttamente (almeno per casi di piccole dimensioni), ma sembra inelegante, non sono sicuro del motivo per cui funziona quando altre cose no, e io Non sono nemmeno sicuro che si comporterebbe ancora come desiderato per casi di grandi dimensioni. Inoltre, non sono sicuro di poter adattare il mio approccio a problemi simili. Non è il caso di me che non capisco il problema, dal momento che ho codificato la stessa cosa in altri linguaggi di programmazione senza problemi.

Quindi mi chiedo come i programmatori Python più esperti possano gestire questo compito generale.

(Sto omettendo il mio codice in parte perché sono nuovo qui e non ho capito come inserirlo su StackOverflow, ma anche perché è long-ish e non voglio aiuto con il problema particolare, ma piuttosto come gestire il procedimento più generale I descritto sopra)

+2

Sarebbe meglio se tu scrivessi il codice. Inoltre, scrivi un esempio concreto che mostra l'input di esempio e l'output previsto –

+0

+1 per un'ottima prima domanda. Sono un po 'poco chiaro cosa intendi, però: "molti di loro modificano non solo l'ultimo elemento aggiunto, ma anche gli elementi aggiunti a L nei passaggi precedenti". Non è questo il tuo comportamento desiderato? Altrimenti, perché stai costruendo questa lista? Puoi dare un esempio più concreto? –

+0

@ ÓscarLópez E 'piuttosto lungo, ma darò una possibilità una volta che ho capito come. Ci ho provato prima, ma è finito in un gran casino, e sono diventato ancora più disordinato quando ho provato a ripulirlo. –

risposta

10

Quando si aggiunge un oggetto lista M ad un'altra lista, sei solo aggiungendo un riferimento.; continuando a manipolare la lista M significa che si vedrà questi cambiamenti riflettono attraverso l'altro di riferimento (s) troppo:

>>> M = [] 
>>> resultlist = [] 
>>> resultlist.append(M) 
>>> M is resultlist[0] 
True 
>>> M.append(1) 
>>> resultlist[0] 
[1] 
>>> M 
[1] 

noti che M is resultlist[0] è vera; è lo stesso oggetto.

dovreste aggiungere un copia di M invece:

resultlist.append(M[:]) 

L'intera fetta qui ([:] significa tagliare dall'inizio alla fine) crea una nuova lista con una copia del contenuto del M .

Il modo generico di generare una serie L da un punto iniziale modificato in modo continuo M consiste nell'utilizzare uno generator function. Il tuo semplice aggiungere il numero accanto a M serie potrebbe essere implementata come:

def growing_sequence(): 
    M = [] 
    counter = 0 
    while True: 
     M.append(counter) 
     counter += 1 
     yield M[:] 

Questo produrrà mai più elenca ogni volta di eseguire iterazioni, su richiesta:

>>> gen = growing_sequence() 
>>> next(gen) 
[0] 
>>> next(gen) 
[0, 1] 
>>> for i, lst in enumerate(gen): 
...  print i, lst 
...  if i == 2: break 
... 
0 [0, 1, 2] 
1 [0, 1, 2, 3] 
2 [0, 1, 2, 3, 4] 
+0

"Quando aggiungi un oggetto lista M a un altro elenco, stai solo aggiungendo un riferimento, continuando a manipolare l'elenco M significa che vedrai quelle modifiche riflesse anche attraverso l'altro/i riferimento/i." - sì. Questo è il problema che stavo affrontando, ma non ero sicuro del miglior linguaggio per descriverlo. Quindi immagino che stavo chiedendo come aggirare questo. Penso che questo risponda alla mia domanda. Per essere sicuro di essere chiaro, "gen" nel tuo codice corrisponde (all'incirca, almeno) a L nella mia domanda? –

+1

@RandyE: invece di produrre una grande lista, 'gen' produce elementi a richiesta (indefinitamente); ma sì, è approssimativamente il tuo 'L', se metti gli elementi prodotti in una lista. –

2

Prova questa:

M = [1] 
L = [M] 
for _ in xrange(3): 
    L += [L[-1] + [L[-1][-1] + 1]] 

Dopo aver eseguito il codice sopra, L conterrà [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]. Spiegazione:

  • Le prime due righe semplicemente seminare l'iterazione con i valori iniziali
  • La linea for afferma quanti cicli vogliamo eseguire dopo che il valore iniziale è stato impostato, 3 in questo caso. Sto usando _ come variabile di iterazione perché non siamo interessati al suo valore, vogliamo solo fare un certo numero di loop

Ora per la parte interessante; e ricorda che in Python un indice negativo in una lista inizia a contare dalla fine, quindi un indice di -1 punta all'ultimo elemento.

  • questo: L += … aggiorna l'elenco, aggiungendo una nuova sottolista alla fine il numero di volte specificato nel ciclo
  • questo: [L[-1] + …] crea un nuovo elenco secondario prendendo l'ultimo sottolista e l'aggiunta di un nuovo elemento al fine
  • e infine questo: [L[-1][-1] + 1] ottiene l'ultimo elemento precedente nella scorsa sottolista, aggiunge uno ad esso e restituisce una lista singolo elemento da concatenato alla fine della precedente espressione
3

questa sarà basata il iterate di Haskell.

iterate :: (a -> a) -> a -> [a]

iterate f x restituisce un elenco infinito di applicazioni ripetute di f-x:

iterate f x == [x, f x, f (f x), ...]

In Python:

def iterate(f, x): 
    while True: 
     yield x 
     x = f(x) 
Utilizzo

Esempio:

>>> import itertools.islice 
>>> def take(n, iterable): 
...  return list(islice(iterable, n)) 

>>> take(4, iterate(lambda x: x + [len(x) + 1], [1])) 
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]] 

Per produrre una lista finita, il tipo di firma (sempre a partire dal Haskell solo per chiarezza) potrebbe essere infiniteFinitely :: (a -> Maybe a) -> a -> [a].

Se dovessimo usare list al posto di Maybe in Python: utilizzo

from itertools import takewhile 

def iterateFinitely(f, x): 
    return map(lambda a: a[0], takewhile(len, iterate(lambda y: f(y[0]), [x]))) 

Esempio:

>>> list(iterateFinitely(lambda x: [x/2] if x else [], 20)) 
[20, 10, 5, 2, 1, 0] 

Dal momento che termina con un valore falsy è probabilmente abbastanza comune, si potrebbe aggiungere anche una versione di questa funzione che lo fa.

def iterateUntilFalsy(f, x): 
    return iterateFinitely(lambda y: [f(y)] if y else [], x) 

Esempio di utilizzo:

>>> list(iterateUntilFalsy(lambda x: x/2, 20)) 
[20, 10, 5, 2, 1, 0] 

>>> list(iterateUntilFalsy(lambda x: x[1:], [1,2,3,4])) 
[[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []] 
+0

L'esempio Haskell fa qualcosa di simile a quello che voglio fare, tranne che in alcuni casi, la lista sarà finita. Questo importa? –

3

si può fare:

M=[1] 
L=[M] 

for e in range(5): 
    li=L[-1][:] 
    li.append(li[-1]+1) 
    L.append(li) 

O più laconicamente:

for e in range(5): 
    L.append(L[-1][:]+[L[-1][-1]+1]) 
3

Penso che il modo migliore per farlo è con a generator. In questo modo, non devi occuparti di list.append, di elenchi di copia in profondità o di simili assurdità.

def my_generator(max): 
    for n in range(max+1): 
    yield list(range(n+1)) 

Poi, basta elencare-Ify esso:

>>> list(my_generator(5)) 
[[0], [0,1], [0,1,2], [0,1,2,3], [0,1,2,3,4], [0,1,2,3,4,5]] 

Questo approccio è anche più flessibile se si voleva rendere un generatore infinita. Basta cambiare il ciclo for per un while true.