Aquí hay dos funciones que dividen los elementos iterables en sub-listas. Creo que este tipo de tarea está programada muchas veces. Los uso para analizar archivos de registro que constan de repr
líneas como ('resultado', 'caso', 123, 4.56) y ('volcado', ..) y así sucesivamente.¿Puede producir múltiples generadores consecutivos?
Me gustaría cambiar esto para que produzcan iteradores en lugar de listas. Porque la lista puede crecer bastante, pero puedo decidir tomarla u omitirla en base a los primeros elementos. Además, si la versión iter está disponible, me gustaría anidarlos, pero con estas versiones de lista que perderían algo de memoria al duplicar partes.
Pero derivar múltiples generadores de una fuente iterativa no me resulta fácil, entonces pido ayuda. Si es posible, deseo evitar la introducción de nuevas clases.
Además, si conoce un título mejor para esta pregunta, por favor dígame.
¡Gracias!
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
edición: mi propia respuesta
Gracias a la respuesta de todo el mundo, podría escribir lo que pedí! Por supuesto, en cuanto a la función "cleave_for_change" también podría usar 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]
PRECAUCIÓN: Si alguien va a usar estos, asegúrese de de escape de los generadores en todos los niveles, como se señaló Andrew. Porque de lo contrario, el bucle externo que genera el generador se reiniciará justo donde el generador interno se fue en lugar de donde comienza el siguiente "bloque".
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')
Si lo que desea es rechazar una lista antes de que sea devuelto o incluso construir, proporcionando un argumento de filtro para las funciones que serían posibles. Cuando este filtro rechaza un prefijo de lista, la función descarta la lista de salida actual y omite agregar a la lista de salida hasta que se inicie el siguiente grupo. –