Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exhaust a list of generators in a cyclic fashion

I have a list of generator functions like:

def myGen(x):
    for i in range(x):
        yield i
g5 = myGen(5); g10 = myGen(10); g15 = myGen(15)
cycleList = [g5, g10, g15]

What is the best way to cycle between these generators and remove the ones that are exhausted from the list?

The output should be:

0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 6 6 7 7 8 8 9 9 10 11 12 13 14
like image 383
engima.bphc Avatar asked Nov 21 '14 11:11

engima.bphc


2 Answers

It looks like you want the roundrobin itertools recipe:

def roundrobin(*iterables):
    "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).next for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

In use:

>>> from itertools import cycle, islice
>>> for i in roundrobin(xrange(5), xrange(10), xrange(15)):
    print i,


0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 6 6 7 7 8 8 9 9 10 11 12 13 14
like image 70
jonrsharpe Avatar answered Nov 09 '22 06:11

jonrsharpe


the round robin recipe is a better way to go but you could also use chain izip_longest and ifilterfalse:

from itertools import chain, izip_longest, ifilterfalse
for x in ifilterfalse(lambda x: x is None,chain.from_iterable(izip_longest(*cycleList))):
        print x,
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 6 6 7 7 8 8 9 9 10 11 12 13 14

If you may have None as a value use object:

my_object = object
for x in ifilterfalse(lambda x: x is my_object,chain.from_iterable(izip_longest(*cycleList,fillvalue=my_object))):
        print x,
like image 32
Padraic Cunningham Avatar answered Nov 09 '22 06:11

Padraic Cunningham