Can yield produce multiple consecutive generators?

自作多情 提交于 2019-12-04 02:00:31

adam's answer is good. this is just in case you're curious how to do it by hand:

def cleave_by_change(stream):
    def generator():
        head = stream[0]
        while stream and stream[0] == head:
            yield stream.pop(0)
    while stream:
        yield generator()

for g in cleave_by_change([1,1,1,2,2,3,2,2,2,2]):
    print list(g)

which gives:

[1, 1, 1]
[2, 2]
[3]
[2, 2, 2, 2]

(previous version required a hack or, in python 3, nonlocal because i assigned to stream inside generator() which made (a second variable also called) stream local to generator() by default - credit to gnibbler in the comments).

note that this approach is dangerous - if you don't "consume" the generators that are returned then you will get more and more, because stream is not getting any smaller.

For your second function, you can use itertools.groupby to accomplish this fairly easily.

Here's an alternate implementation that now yields generators instead of lists:

from itertools import groupby

def cleave_by_change2(stream, key_fn):
    return (group for key, group in groupby(stream, key_fn))

Here is it in action (with liberal printing along the way, so you can see what's going on):

main_gen = cleave_by_change2([1,1,1,2,2,3,2,2,2,2], lambda x: x)

print main_gen

for sub_gen in main_gen:
    print sub_gen
    print list(sub_gen)

Which yields:

<generator object <genexpr> at 0x7f17c7727e60>
<itertools._grouper object at 0x7f17c77247d0>
[1, 1, 1]
<itertools._grouper object at 0x7f17c7724850>
[2, 2]
<itertools._grouper object at 0x7f17c77247d0>
[3]
<itertools._grouper object at 0x7f17c7724850>
[2, 2, 2, 2]

I implemented what I described:

If what you want is to reject a list before it is returned or even build, by providing a filter argument to the functions that would be possible. When this filter rejects a list prefix the function would toss out the current output list and skip appending to the output list until the next group is started.

def cleave_by_change (stream, key_fn, filter=None):
    '''[1 1 1][2 2][3][2 2 2 2]'''
    S = object()
    skip = False
    prev = S
    buf = []
    for item in stream:
        iden = key_fn(item)
        if prev is S:
           prev = iden
        if prev != iden:
            if not skip:
                yield buf
            buf = []
            prev = iden
            skip = False
        if not skip and filter is not None:
           skip = not filter(item)
        if not skip:
           buf.append(item)
    if buf: yield buf

print list(cleave_by_change([1, 1, 1, 2, 2, 3, 2, 2, 2, 2], lambda a: a, lambda i: i != 2))
# => [[1, 1, 1], [3]]
print list(cleave_by_change([1, 1, 1, 2, 2, 3, 2, 2, 2, 2], lambda a: a, lambda i: i == 2))
# => [[2, 2], [2, 2, 2, 2]]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!