Qui ci sono due funzioni che dividono gli articoli iterabili in sotto-liste. Credo che questo tipo di compito sia programmato molte volte. Li utilizzo per analizzare i file di registro costituiti da righe repr
come ('risultato', 'caso', 123, 4.56) e ('dump', ..) e così via.È possibile produrre più generatori consecutivi?
Vorrei cambiare questi in modo che producano iteratori anziché elenchi. Perché l'elenco potrebbe diventare abbastanza grande, ma potrei essere in grado di decidere di prenderlo o saltarlo in base ai primi pochi elementi. Inoltre, se la versione iter è disponibile, vorrei accoppiarli, ma con queste versioni di elenchi che sprecherebbero un po 'di memoria duplicando parti.
Ma non è facile per me generare più generatori da una fonte iterabile, quindi chiedo aiuto. Se possibile, desidero evitare di introdurre nuove classi.
Inoltre, se si conosce un titolo migliore per questa domanda, per favore dimmi.
Grazie!
def cleave_by_mark (stream, key_fn, end_with_mark=False):
'''[f f t][t][f f] (true) [f f][t][t f f](false)'''
buf = []
for item in stream:
if key_fn(item):
if end_with_mark: buf.append(item)
if buf: yield buf
buf = []
if end_with_mark: continue
buf.append(item)
if buf: yield buf
def cleave_by_change (stream, key_fn):
'''[1 1 1][2 2][3][2 2 2 2]'''
prev = None
buf = []
for item in stream:
iden = key_fn(item)
if prev is None: prev = iden
if prev != iden:
yield buf
buf = []
prev = iden
buf.append(item)
if buf: yield buf
edit: la mia risposta personale
Grazie alla risposta di tutti, ho potuto scrivere quello che ho chiesto! Ovviamente, come per la funzione "cleave_for_change", potrei usare anche itertools.groupby
.
def cleave_by_mark (stream, key_fn, end_with_mark=False):
hand = []
def gen():
key = key_fn(hand[0])
yield hand.pop(0)
while 1:
if end_with_mark and key: break
hand.append(stream.next())
key = key_fn(hand[0])
if (not end_with_mark) and key: break
yield hand.pop(0)
while 1:
# allow StopIteration in the main loop
if not hand: hand.append(stream.next())
yield gen()
for cl in cleave_by_mark (iter((1,0,0,1,1,0)), lambda x:x):
print list(cl), # start with 1
# -> [1, 0, 0] [1] [1, 0]
for cl in cleave_by_mark (iter((0,1,0,0,1,1,0)), lambda x:x):
print list(cl),
# -> [0] [1, 0, 0] [1] [1, 0]
for cl in cleave_by_mark (iter((1,0,0,1,1,0)), lambda x:x, True):
print list(cl), # end with 1
# -> [1] [0, 0, 1] [1] [0]
for cl in cleave_by_mark (iter((0,1,0,0,1,1,0)), lambda x:x, True):
print list(cl),
# -> [0, 1] [0, 0, 1] [1] [0]
/
def cleave_by_change (stream, key_fn):
'''[1 1 1][2 2][3][2 2 2 2]'''
hand = []
def gen():
headkey = key_fn(hand[0])
yield hand.pop(0)
while 1:
hand.append(stream.next())
key = key_fn(hand[0])
if key != headkey: break
yield hand.pop(0)
while 1:
# allow StopIteration in the main loop
if not hand: hand.append(stream.next())
yield gen()
for cl in cleave_by_change (iter((1,1,1,2,2,2,3,2)), lambda x:x):
print list(cl),
# -> [1, 1, 1] [2, 2, 2] [3] [2]
ATTENZIONE: Se qualcuno sta andando a utilizzare questi, assicurati di scarico generatori a tutti i livelli, come Andrew ha sottolineato. Perché altrimenti il ciclo di generazione del generatore esterno si riavvierà proprio dove il generatore interno è rimasto invece di dove inizia il prossimo "blocco".
stream = itertools.product('abc','1234', 'ABCD')
for a in iters.cleave_by_change(stream, lambda x:x[0]):
for b in iters.cleave_by_change(a, lambda x:x[1]):
print b.next()
for sink in b: pass
for sink in a: pass
('a', '1', 'A')
('b', '1', 'A')
('c', '1', 'A')
Se quello che vuoi è quello di respingere una lista prima di essere restituita o addirittura costruire, fornendo un argomento filtro per le funzioni che sarebbero possibili. Quando questo filtro rifiuta un prefisso di elenco, la funzione eliminerà l'elenco di output corrente e salterà l'aggiunta all'elenco di output fino all'avvio del gruppo successivo. –