Как элегантно использовать итераторы Python - PullRequest
4 голосов
/ 10 декабря 2010

Я пытаюсь использовать итераторы для цикла, так как слышал, что это быстрее, чем индексирование. Одна вещь, в которой я не уверен, заключается в том, как хорошо относиться к концу последовательности. Я могу думать о том, чтобы использовать try и except StopIteration, что мне некрасиво.

Чтобы быть более конкретным, предположим, что нас просят напечатать объединенный отсортированный список из двух отсортированных списков a и b. Я бы написал следующее

aNull = False
I = iter(a)
try:
    tmp = I.next()
except StopIteration:
    aNull = True

for x in b:
    if aNull:
        print x
    else:
        if x < tmp:
            print x
        else:
            print tmp,x
            try:
                tmp = I.next()
            except StopIteration:
                aNull = True

while not aNull:
    print tmp
    try:
        tmp = I.next()
    except StopIteration:
        aNull = True

Как бы вы написали код, чтобы сделать его аккуратнее?

Ответы [ 3 ]

8 голосов
/ 10 декабря 2010

Я думаю, что обработка a и b более симметрично облегчит чтение. Кроме того, использование встроенной функции next в Python 2.6 со значением по умолчанию избавляет от необходимости обрабатывать StopIteration:

def merge(a, b):
    """Merges two iterators a and b, returning a single iterator that yields
    the elements of a and b in non-decreasing order.  a and b are assumed to each
    yield their elements in non-decreasing order."""

    done = object()
    aNext = next(a, done)
    bNext = next(b, done)

    while (aNext is not done) or (bNext is not done):
        if (bNext is done) or ((aNext is not done) and (aNext < bNext)):
            yield aNext
            aNext = next(a, done)
        else:
            yield bNext
            bNext = next(b, done)

for i in merge(iter(a), iter(b)):
    print i

Следующая функция обобщает подход к работе для произвольного числа итераторов.

def merge(*iterators):
    """Merges a collection of iterators, returning a single iterator that yields
    the elements of the original iterators in non-decreasing order.  Each of
    the original iterators is assumed to yield its elements in non-decreasing
    order."""

    done = object()
    n = [next(it, done) for it in iterators]

    while any(v is not done for v in n):
        v, i = min((v, i) for (i, v) in enumerate(n) if v is not done)
        yield v
        n[i] = next(iterators[i], done)
6 голосов
/ 10 декабря 2010

Вам не хватает всего смысла итераторов.Вы не вызываете I.next() вручную, вы просто перебираете I.

for tmp in I:
    print tmp

Отредактировано

Чтобы объединить два итератора, используйте очень удобные функциив модуле itertools.Вероятно, вам понадобится izip:

merged = []
for x, y in itertools.izip(a, b):
    if x < y:
        merged.append(x)
        merged.append(y)
    else:
        merged.append(y)
        merged.append(x)

Изменить еще раз

Как указано в комментариях, это нена самом деле работает, потому что может быть несколько элементов из списка меньше, чем следующий элемент в списке b.Однако я понял, что есть еще одна встроенная функция, которая имеет дело с этим: heapq.merge.

0 голосов
/ 10 декабря 2010

Функция sorted работает со списками и итераторами.Может быть, это не то, что вы хотите, но следующий код работает.


a.expand(b)
print sorted(iter(a))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...