Самый хороший и эффективный способ получить набор результатов последовательности элементов, выполняющих и не выполняющих условие

(Это профессиональная практика / интерес, а не домашняя работа)

  • INPUT: любая неупорядоченная последовательность или элементы генератора, функция myfilter(item) возвращает True, если условие фильтра выполнено

  • ВЫХОД: (filter_true, filter_false) кортеж последовательностей оригинального типа, которые содержат элементы, разделенные в соответствии с фильтром в оригинальном порядке следования.

Как бы вы выразили это без двойной фильтрации, или я должен использовать двойную фильтрацию? Может быть, фильтр и цикл / генератор / список понимания с next может быть ответ?

Должен ли я отменить требование сохранения типа или просто изменить требование, дающее кортеж результата кортеж / генератор, я не могу легко вернуть генератор для ввода генератора, или я могу? (Требования сделаны самостоятельно)

Здесь тест лучшего кандидата на данный момент, предлагающий два потока вместо кортежа

import itertools as it
from sympy.ntheory import isprime as myfilter

mylist = xrange(1000001,1010000,2)
left,right = it.tee((myfilter(x), x) for x in mylist)
filter_true = (x for p,x in left if p)
filter_false = (x for p,x in right if not p)

print 'Hundred primes and non-primes odd  numbers'
print  '\n'.join( " Prime %i, not prime %i" %
                  (next(filter_true),next(filter_false))
                  for i in range(100))

4 ответа

Решение

Вот способ сделать это, который только вызывает myfilter один раз для каждого элемента, а также будет работать, если mylist это генератор

import itertools as it
left,right = it.tee((myfilter(x), x) for x in mylist)
filter_true = (x for p,x in left if p)
filter_false = (x for p,x in right if not p)

Предположим, что ваша проблема не в памяти, а в процессоре, myfilter имеет большой вес, и вы не хотите повторять и фильтровать исходный набор данных дважды. Вот несколько однопроходных идей:

Простая и универсальная версия (с памятью):

filter_true=[]
filter_false=[]
for item in  items:
    if myfilter(item):
        filter_true.append(item)
    else:
        filter_false.append(item)  

Версия, ориентированная на память: (не работает с генераторами (если не используется со списком (элементами)))

while items:
    item=items.pop()
    if myfilter(item):
        filter_true.append(item)
    else:
        filter_false.append(item)  

Версия для генератора:

while True:
    try:
        item=next(items)
        if myfilter(item):
            filter_true.append(item)
        else:
            filter_false.append(item)  
    except StopIteration:
        break

Самый простой способ (но менее эффективный) tee повторяем и фильтруем оба из них:

import itertools
left, right = itertools.tee( mylist )
filter_true = (x for x in left if myfilter(x))
filter_false = (x for x in right if myfilter(x))

Это менее эффективно, чем оптимальное решение, потому что myfilter будет вызываться повторно для каждого элемента. То есть, если вы проверили элемент в left Вы не должны повторно тестировать его в right потому что вы уже знаете ответ. Если вам требуется эта оптимизация, ее не должно быть сложно реализовать: взгляните на реализацию tee для подсказок. Вам понадобится дек для каждой возвращаемой итерации, которую вы снабжаете элементами оригинальной последовательности, которые должны войти в нее, но еще не были запрошены.

Я думаю, что ваша лучшая ставка будет создавать два отдельных генератора:

filter_true = (x for x in mylist if myfilter(x))
filter_false = (x for x in mylist if not myfilter(x))
Другие вопросы по тегам