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

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

  • INPUT : любая неупорядоченная последовательность или генератор items , функция 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 ]

5 голосов
/ 06 сентября 2010

Вот способ сделать это, который вызывает 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)
2 голосов
/ 06 сентября 2010

Предположим, что ваша проблема не в памяти, а в процессоре, 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
0 голосов
/ 06 сентября 2010

Простой способ (но менее эффективный) - это 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 для подсказок. Вам понадобится дек для каждой возвращаемой итерации, которую вы снабжаете элементами оригинальной последовательности, которые должны быть в ней, но еще не запрошены.

0 голосов
/ 06 сентября 2010

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

filter_true = (x for x in mylist if myfilter(x))
filter_false = (x for x in mylist if not myfilter(x))
...