Я очень часто сталкиваюсь с необходимостью разбить последовательность на две подпоследовательности элементов, которые удовлетворяют и не удовлетворяют заданному предикату (сохраняя исходное относительное упорядочение).
Эта гипотетическая функция "сплиттера" будет выглядеть примерно так:
>>> data = map(str, range(14))
>>> pred = lambda i: int(i) % 3 == 2
>>> splitter(data, pred)
[('2', '5', '8', '11'), ('0', '1', '3', '4', '6', '7', '9', '10', '12', '13')]
Мой вопрос:
У Python уже есть стандартный/встроенный способ сделать это?
Эту функциональность, конечно, не сложно скомпоновать (см. добавление ниже), но по ряду причин я предпочел бы использовать стандартный/встроенный метод, чем самокалиброванный.
Спасибо!
Добавление:
Лучшая стандартная функция, которую я нашел до сих пор для обработки этой задачи в Python, - itertools.groupby
. Однако, чтобы использовать его для этой конкретной задачи, необходимо дважды вызвать функцию предиката для каждого члена списка, что я считаю досадно глупым:
>>> import itertools as it
>>> [tuple(v[1]) for v in it.groupby(sorted(data, key=pred), key=pred)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
(Последний вывод выше отличается от желаемого, показанного ранее тем, что подпоследовательность элементов, которые удовлетворяют предикату, появляется скорее, чем первая, но это очень незначительно и очень легко исправить при необходимости.)
Можно избежать избыточных вызовов предиката (делая, в основном, "встроенную memoization" ), но мой лучший удар по этому поводу становится довольно сложным, далеким от простоты splitter(data, pred)
:
>>> first = lambda t: t[0]
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data),
... key=first), key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]
Кстати, если вы не заботитесь о сохранении исходного заказа, sorted
порядок сортировки по умолчанию получает задание (поэтому параметр key
может быть пропущен в вызове sorted
):
>>> [zip(*i[1])[1] for i in it.groupby(sorted(((pred(x), x) for x in data)),
... key=first)]
[('0', '1', '3', '4', '6', '7', '9', '10', '12', '13'), ('2', '5', '8', '11')]