multirember&co

bearophileHUGS at lycos.com bearophileHUGS at lycos.com
Wed Apr 18 00:52:33 CEST 2007


Paddy:

> def multiremberandco1(el, seq, fun):
>     l1, l2 = [], []
>     c = seq.count(e1)
>     l1 = [el] * c
>     l2 = [el] * (len(seq) - c)
>     return fun(l1, l2)


Thank you Paddy, but unfortunately there is a bug in my first
function, this is more correct:

def multiremberandco1b(el, seq, fun):
    l1, l2 = [], []
    for x in seq:
        if x == el:
            l2.append(x)
        else:
            l1.append(x)
    return fun(l1, l2)


So your version may become:

def multiremberandco1c(el, seq, fun):
    l2 = [el] * seq.count(el)
    l1 = filter(lambda x: x != el, seq)
    return fun(l1, l2)

-------------------

I am grateful to everyone that has answered me, but Steven has
invented what I was looking for. It's code quite twisted on itself :-)
I have modified, simplified and (hopefully) improved Steven's code
like this (but it may be a bit slower, because the class It is inside
the function?):


import collections

def xsplitter(iseq, pred):
    class It(object):
        def __init__(self, fun, parity):
            self.divert = fun
            self.parity = parity
            self.queue = collections.deque()
        def append(self, item):
            self.queue.append(item)
        def __iter__(self):
            while True:
                if self.queue:
                    yield self.queue.popleft()
                else:
                    try:
                        item = iseq.next()
                        if pred(item) == self.parity:
                            yield item
                        else:
                            self.divert(item)
                    except StopIteration:
                        raise

    it1 = It(lambda y: it2.append(y), True)
    it2 = It(lambda y: it1.append(y), False)
    return it1, it2

idata = iter([1, 'a', 3, 'a', 4, 5, 6, 'a'])
it1, it2 = xsplitter(idata, lambda x: x == 'a')

from itertools import izip
for el1, el2 in izip(it1, it2):
    print el1, el2


(The izip stops when the first iterable ends, with while-True loop and
more code that they can be printed fully). Now this code is the lazy
splitter I was talking about. I don't know if it can be useful, but I
like it enough. Probably it's useless if one iterable is used whole
before the other one (because a deque becomes filled up more and
more), so I think in most situations a much simpler eager list
splitter is enough. So I don't know if an improved version of it is
fit for the itertools module.

So far I haven't succed using the coroutine Python 2.5 allows using
the generators, and I think still that xsplitter can be done with two
coroutines instead of two It objects. Despite Steven's code I am
unable still to write a working code with coroutines (probably because
I haven't understood how they work yet). This time I take a breath and
I show my wrong code:


import collections

def xsplitter(iseq, pred):
    def it(parity):
        queue = collections.deque()
        while True:
            received = (yield)
            if received is not None:
                queue.append(received)
            if queue:
                yield queue.popleft()
            else:
                try:
                    el = iseq.next()
                    if pred(el) == parity:
                        yield el
                    else:
                        its[not parity].send(el)
                except StopIteration:
                    raise

    its = [it(False), it(True)]
    return its[True], its[False]

idata = iter([1, 'a', 3, 'a', 4, 5, 6, 'a'])
it1, it2 = xsplitter(idata, lambda x: x == 'a')

from itertools import izip
for el1, el2 in izip(it1, it2):
    print el1, el2


It prints:
None None
None None
a 3
None None
a 4
None None
None None
None None


Can it be fixed? Are you able to fix it for me? (If you want you can
think of it as an exercise to understand Python coroutines :-) ).

Bye and thank you,
bearophile




More information about the Python-list mailing list