Python: Изящный способ двойного/нескольких повторения по тому же списку

Функция сохранения имеет обратный вызов. Попробуйте использовать это и проверьте, есть ли какая-либо ошибка, которую можно отследить. Вы можете сделать что-то вроде этого.

user.save(function (err) { 
    if (err) return handleError(err); 
    // saved!
  });
6
задан EoghanM 15 May 2009 в 11:58
поделиться

17 ответов

Насколько я могу судить, какое-либо из существующих решений работает над общим одним выстрелом, возможно, бесконечным итератором , все они, похоже, требуют итера способный .

Вот решение.

def batch_by(condition, seq):
    it = iter(seq)
    batch = [it.next()]
    for jump_item in it:
        if condition(jump_item):
            for item in batch:
                yield item, jump_item
            batch = []
        batch.append(jump_item)

Это будет легко работать на бесконечных итераторах:

from itertools import count, islice
is_prime = lambda n: n == 2 or all(n % div for div in xrange(2,n))
print list(islice(batch_by(is_prime, count()), 100))

Это напечатает первые 100 целых чисел с простым числом, которое следует за ними.

4
ответ дан 16 December 2019 в 21:45
поделиться

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

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
ответ дан 16 December 2019 в 21:45
поделиться

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

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)

, а затем просто напишите два цикла более элементов .

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

0
ответ дан 16 December 2019 в 21:45
поделиться

Вы можете поместить всю итерацию в одну структуру попыток, чтобы было понятнее:

jump_item_iter = (j for j in items if some_cond)
try:
    jump_item = jump_item_iter.next()
    for item in items:
        if jump_item is item:
            jump_item = jump_iter.next()

    # do lots of stuff with item and jump_item

 except StopIteration:
     pass
0
ответ дан 16 December 2019 в 21:45
поделиться

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

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

drop while вернет итератор, который пропускает все те, которые соответствуют условию (здесь « - это элемент »).

0
ответ дан 16 December 2019 в 21:45
поделиться

Может быть, уже слишком поздно, но как насчет:

l = [j for j in items if some_cond]
for item, jump_item in zip(l, l[1:]):
    # do lots of stuff with item and jump_item

Если l = [j для j в диапазоне (10), если j% 2 == 0], то итерация завершена: [ (0, 2), (2, 4), (4, 6), (6, 8)].

0
ответ дан 16 December 2019 в 21:45
поделиться

Только итераторы

def(lst, some_cond):
      jump_item_iter = (j for j in lst if som_cond(j))
      pairs = itertools.izip(lst, lst[1:])
      for last in jump_item_iter:
        for start, start_next in itertools.takewhile(lambda pair: pair[0] < last, pairs):
          yield start, last
        pairs = itertools.chain([(start_next, 'dummy')], pairs)

с вводом: range (10) и some_cond = lambda x: x% 2 дает [(0, 1), (1, 3), (2, 3), (3, 5), (4, 5), (5, 7), (6, 7), (7, 9), (8, 9)] (то же, что и ваш пример)

0
ответ дан 16 December 2019 в 21:45
поделиться
for i in range( 0, len( items ) ):
    for j in range( i+1, len( items ) ):
        if some_cond:
            #do something
            #items[i] = item, items[j] = jump_item
0
ответ дан 16 December 2019 в 21:45
поделиться

Еще лучше использовать itertools.groupby:

def h(lst, cond):
  remain = lst
  for last in (l for l in lst if cond(l)):
    group = itertools.groupby(remain, key=lambda x: x < last)
    for start in group.next()[1]:
      yield start, last
    remain = list(group.next()[1])

Использование: lst = диапазон (10) cond = лямбда x: x% 2 print list (h (lst, cond))

напечатает

[(0, 1), (1, 3), (2, 3), (3, 5), (4, 5), (5, 7), (6, 7), (7, 9), (8, 9)]
0
ответ дан 16 December 2019 в 21:45
поделиться

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

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


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
ответ дан 16 December 2019 в 21:45
поделиться

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

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

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

0
ответ дан 16 December 2019 в 21:45
поделиться

Как насчет этого?

paired_values = []
for elmt in reversed(items):
    if <condition>:
        current_val = elmt
    try:
        paired_values.append(current_val)
    except NameError:  # for the last elements of items that don't pass the condition
        pass
paired_values.reverse()

for (item, jump_item) in zip(items, paired_values):  # zip() truncates to len(paired_values)
    # do lots of stuff

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

1
ответ дан 16 December 2019 в 21:45
поделиться

Следующий итератор эффективен по времени и памяти:

def jump_items(items):
    number_to_be_returned = 0
    for elmt in items:
        if <condition(elmt)>:
            for i in range(number_to_be_returned):
                yield elmt
            number_to_be_returned = 1
        else:
            number_to_be_returned += 1

for (item, jump_item) in zip(items, jump_items(items)):
    # do lots of stuff

Обратите внимание, что на самом деле вы можете захотеть установить для первого number_to_be_returned значение 1 ...

1
ответ дан 16 December 2019 в 21:45
поделиться

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

0
ответ дан 16 December 2019 в 21:45
поделиться

Напишите функцию генератора:

def myIterator(someValue):
    yield (someValue[0], someValue[1])

for element1, element2 in myIterator(array):
     # do something with those elements.
1
ответ дан 16 December 2019 в 21:45
поделиться

Я понятия не имею, что делает compare () , но в 80% случаев вы можете сделать это с помощью простого словаря или пары словарей. Перемещение по списку - это своего рода линейный поиск. Линейный поиск - насколько это возможно - всегда следует заменять либо прямой ссылкой (например, dict), либо поиском по дереву (с использованием модуля bisect).

1
ответ дан 16 December 2019 в 21:45
поделиться

Мой первый ответ был неправильным, потому что я не совсем понимал, чего вы пытались достичь. Итак, если я правильно понимаю (на этот раз, надеюсь), вы хотите, чтобы основной для элемента в items: «преследовал» итератор, который отфильтровывает некоторые элементы. Что ж, вы мало что можете сделать, за исключением, может быть, обернуть это в генератор chase_iterator (iterable, some_cond) , который сделает ваш основной код немного более читаемым.

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

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

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

0
ответ дан 16 December 2019 в 21:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: