Oto dwie funkcje, które dzielą elementy iterowalne na listy podrzędne. Wierzę, że ten typ zadania jest programowany wiele razy. Używam ich do analizowania plików dziennika, które składają się z linii repr
, takich jak ("wynik", "case", 123, 4.56) i ("dump", ..) i tak dalej.Czy wydajność może generować wiele kolejnych generatorów?
Chciałbym je zmienić, aby były raczej iteratorami niż listami. Ponieważ lista może być dość duża, ale mogę zdecydować się ją przyjąć lub pominąć na podstawie kilku pierwszych elementów. Ponadto, jeśli wersja iter jest dostępna, chciałbym je zagnieździć, ale z tymi wersjami list, które mogłyby zmarnować część pamięci przez powielenie części.
Ale wyprowadzenie wielu generatorów z iterowanego źródła byłoby dla mnie łatwe, więc proszę o pomoc. Jeśli to możliwe, chcę uniknąć wprowadzania nowych zajęć.
Ponadto, jeśli znasz lepszy tytuł dla tego pytania, proszę powiedz mi.
Dziękujemy!
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: moja własna odpowiedź
Dzięki wszystkim za odpowiedzi, mogę napisać co prosiłem! Oczywiście, jeśli chodzi o funkcję "cleave_for_change", mógłbym także użyć 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]
UWAGA: Jeśli ktoś zamierza z nich korzystać, należy spalin generatorów na każdym poziomie, jak Andrew zauważył. Ponieważ w przeciwnym razie zewnętrzna pętla generująca generator zostanie ponownie uruchomiona w miejscu, w którym pozostawiono generator wewnętrzny zamiast miejsca, w którym zaczyna się następny "blok".
Jeśli to, co chcesz, aby odrzucić listę zanim zostanie zwrócone lub nawet budować, dostarczając argumentu filtra do funkcji, które byłyby możliwe. Kiedy ten filtr odrzuci prefiks listy, funkcja rzuciłaby bieżącą listę wyników i pominęła dodawanie do listy wyjściowej, dopóki nie zostanie uruchomiona następna grupa. –