Python: элегантный способ двойной / многократной итерации по одному и тому же списку - PullRequest
6 голосов
/ 15 мая 2009

Я написал немного кода, подобного следующему, чтобы сравнить элементы с другими элементами в списке далее. Есть ли более элегантный шаблон для такой двойной итерации?

jump_item_iter = (j for j in items if some_cond)
try:
    jump_item = jump_item_iter.next()
except StopIteration:
    return
for item in items:
    if jump_item is item:
        try:
            jump_item = jump_iter.next()
        except StopIteration:
            return
    # do lots of stuff with item and jump_item

Я не думаю, что "except StopIteration" очень элегантно

Edit:

Чтобы, надеюсь, стало понятнее, я хочу посетить каждый элемент в списке и связать его со следующим элементом в списке (jump_item), который удовлетворяет some_cond.

Ответы [ 17 ]

0 голосов
/ 15 мая 2009

Мой первый ответ был неверным, потому что я не совсем понял, чего вы пытались достичь. Поэтому, если я правильно понимаю (на этот раз, я надеюсь), вы хотите, чтобы основной for item in items: "преследовал" после итератора, который отфильтровывает некоторые элементы. Ну, вы ничего не можете сделать, кроме, может быть, обернуть это в chase_iterator(iterable, some_cond) генератор, который сделает ваш основной код немного более читабельным.

Возможно, что более читаемый подход был бы "аккумуляторным подходом" (если порядок сравнения () не имеет значения), например:

others = []
for item in items:
    if some_cond(item):
        for other in others:
            compare(item, other)
        others = []
    else:
        others.append(item)

(чувак, я начинаю ненавидеть переполнение стека ... слишком затягивает ...)

0 голосов
/ 15 мая 2009

Вот одно простое решение, которое может выглядеть немного чище:

for i, item in enumerate(items):
    for next_item in items[i+1:]:
        if some_cond(next_item):
            break
    # do some stuff with both items

Недостатком является то, что вы проверяете условие для next_item несколько раз. Но вы можете легко оптимизировать это:

cond_items = [item if some_cond(item) else None for item in items]
for i, item in enumerate(items):
    for next_item in cond_items[i+1:]:
        if next_item is not None:
            break
    # do some stuff with both items

Однако оба решения несут больше накладных расходов, чем исходное решение вопроса. И когда вы начинаете использовать счетчики, чтобы обойти это, тогда я думаю, что лучше использовать интерфейс итератора напрямую (как в исходном решении).

0 голосов
/ 15 мая 2009

Вы в основном пытаетесь сравнить каждый элемент в итераторе с каждым другим элементом в исходном списке?

На мой взгляд, это просто случай использования двух циклов, а не попытка вписать их в один.


filtered_items = (j for j in items if some_cond)
for filtered in filtered_items:
    for item in items:
        if filtered != item:
            compare(filtered, item)

0 голосов
/ 15 мая 2009

Таким образом, вы хотите сравнить пары элементов в одном и том же списке, второй элемент пары должен удовлетворять некоторому условию. Обычно, когда вы хотите сравнить пары в списке, используйте zip (или itertools.izip):

for item1, item2 in zip(items, items[1:]):
    compare(item1, item2)

Выясните, как вписать ваш some_cond в это:)

0 голосов
/ 15 мая 2009

Вы можете написать ваше тело цикла как:

import itertools, functools, operator

for item in items:
    jump_item_iter = itertools.dropwhile(functools.partial(operator.is_, item), 
                                         jump_item_iter)

    # do something with item and jump_item_iter

dropwise возвращает итератор, который пропускает все те, которые соответствуют условию (здесь "is item").

0 голосов
/ 16 мая 2009

Вы можете сделать что-то вроде:

import itertools

def matcher(iterable, compare):
    iterator= iter(iterable)
    while True:
        try: item= iterator.next()
        except StopIteration: break
        iterator, iterator2= itertools.tee(iterator)
        for item2 in iterator2:
            if compare(item, item2):
                yield item, item2

но он довольно сложный (и на самом деле не очень эффективный), и было бы проще, если бы вы только что сделали

items= list(iterable)

, а затем просто записать два цикла над items.

Очевидно, что это не будет работать с бесконечными итерациями, но ваша спецификация может работать только с конечными итерациями.

0 голосов
/ 15 мая 2009

Понятия не имею, что вы пытаетесь сделать с этим кодом. Но я на 99% уверен, что все, что бы это ни было, может быть сделано в 2 строки. У меня также возникает ощущение, что оператор '==' должен быть оператором 'is', иначе что делает функция compare ()? А что произойдет, если элемент, возвращенный из второго вызова jump_iter.next, также будет равен 'item'? Похоже, алгоритм будет работать неправильно, поскольку вы будете сравнивать второе, а не первое.

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