[Python Wpg] Some beautiful code

Stuart Williams stuartw at mts.net
Thu Feb 1 22:58:58 EST 2007


Here's a snippet of code we looked at at the last meeting.  To
understand it by example see the tests at the bottom.

I made the comment at the meeting that when I rewrote it to be more
general it was much shorter and simpler.

def old_generate_matches(itera, iterb):
    """Take two iterables which return tuples (key, content).
    Return pairs of items (as a tuple) that share the key
    or an item paired with None if a matching element is missing."""

    needed = object()                   # unique sentinel, so None is allowed in set
    a, b = needed, needed
    
    while True:
        if a is needed:
            try:
                keya, a = itera.next()
                # if a is None:
                #     raise ValueError('Iterable cannot contain None')
            except StopIteration:
                while True:
                    try:
                        keyb, b = iterb.next()
                        yield (None, b)
                    except StopIteration:
                        return
                    
        if b is needed:
            try:
                keyb, b = iterb.next()
                # if b is None:
                #     raise ValueError('Iterable cannot contain None')
            except StopIteration:
                yield (a, None) # we aleady have an A from try above
                while True:
                    try:
                        keya, a = itera.next()
                        yield (a, None)
                    except StopIteration:
                        return

        if keya == keyb:
            yield (a, b)
            a, b = needed, needed
        elif keya > keyb:
            yield (None, b)
            b = needed
        else:
            yield (a, None)
            a = needed

def generate_matches(*iters):
    """Take N iterables which return tuples (key, content).
    Return tuples of items that share the key, filling with
    with None if a matching element is missing."""

    nelements = len(iters)
    needed = object()                   # a sentinel that's better than None
    done = object()
    alldone = [done] * nelements
    keys = [needed] * nelements
    elements = [None] * nelements
    
    while True:                         # always at least one needed when we enter
        for i in range(nelements):
            if keys[i] is needed:
                try:
                    keys[i], elements[i] = iters[i].next()
                except StopIteration:
                    keys[i] = done
                    elements[i] = None
        # we now have 3 values, maybe less, maybe all done
        if keys == alldone:
            break

        minkey = min(keys)
        results = [None] * nelements
        for i in range(nelements):
            if keys[i] == minkey:
                results[i] = elements[i]
                keys[i] = needed
        yield results

def gen_gm(a, b):
    return (iter([(int(e), int(e)) for e in a.split()]),
            iter([(int(e), int(e)) for e in b.split()]))

def test_generate_matches():
    assert (list(generate_matches(*gen_gm('1 2 3', '1 2 3')))
            == [[1, 1], [2, 2], [3, 3]])

    assert (list(generate_matches(*gen_gm('1 2', '1 2 3')))
            == [[1, 1], [2, 2], [None, 3]])

    assert (list(generate_matches(*gen_gm('1 2 3', '1 2')))
            == [[1, 1], [2, 2], [3, None]])

    assert (list(generate_matches(*gen_gm('1 2 4 7 8', '1 2 3 5 6 8')))
            == [[1, 1], [2, 2], [None, 3], [4, None], [None, 5], [None, 6], [7, None], [8, 8]])



More information about the Winnipeg mailing list