Самый хороший и эффективный способ получить набор результатов последовательности элементов, выполняющих и не выполняющих условие
(Это профессиональная практика / интерес, а не домашняя работа)
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))