разделить генератор / повторяемый каждые n элементов в Python (splitEvery) - PullRequest
29 голосов
/ 16 декабря 2009

Я пытаюсь написать функцию Haskel 'splitEvery' на Python. Вот его определение:

splitEvery :: Int -> [e] -> [[e]]
    @'splitEvery' n@ splits a list into length-n pieces.  The last
    piece will be shorter if @n@ does not evenly divide the length of
    the list.

Базовая версия этого прекрасно работает, но я хочу версию, которая работает с выражениями генератора, списками и итераторами. И , если в качестве входа используется генератор, он должен возвращать генератор в качестве выхода!

Тесты

# should not enter infinite loop with generators or lists
splitEvery(itertools.count(), 10)
splitEvery(range(1000), 10)

# last piece must be shorter if n does not evenly divide
assert splitEvery(5, range(9)) == [[0, 1, 2, 3, 4], [5, 6, 7, 8]]

# should give same correct results with generators
tmp = itertools.islice(itertools.count(), 10)
assert list(splitEvery(5, tmp)) == [[0, 1, 2, 3, 4], [5, 6, 7, 8]]

Текущая реализация

Вот код, который у меня сейчас есть, но он не работает с простым списком.

def splitEvery_1(n, iterable):
    res = list(itertools.islice(iterable, n))
    while len(res) != 0:
        yield res
        res = list(itertools.islice(iterable, n))

Это не работает с выражением генератора (спасибо jellybean за исправление):

def splitEvery_2(n, iterable): 
    return [iterable[i:i+n] for i in range(0, len(iterable), n)]

Должен быть простой фрагмент кода, который выполняет разбиение. Я знаю, что у меня могут быть разные функции, но кажется, что это должно быть легко сделать. Я, вероятно, застреваю на неважной проблеме, но это действительно беспокоит меня.


Это похоже на grouper из http://docs.python.org/library/itertools.html#itertools.groupby, но я не хочу, чтобы он заполнял дополнительные значения.

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

В нем упоминается метод, который усекает последнее значение. Это тоже не то, чего я хочу.

Порядок вычислений итераций слева направо гарантирован. Это делает возможным объединение ряда данных в группы n-длины с использованием izip (* [iter (s)] * n).

list(izip(*[iter(range(9))]*5)) == [[0, 1, 2, 3, 4]]
# should be [[0, 1, 2, 3, 4], [5, 6, 7, 8]]

Ответы [ 13 ]

0 голосов
/ 20 июля 2012

это сделает свое дело

from itertools import izip_longest
izip_longest(it[::2], it[1::2])

где * это * итерация


Пример:

izip_longest('abcdef'[::2], 'abcdef'[1::2]) -> ('a', 'b'), ('c', 'd'), ('e', 'f')

Давайте разберемся с этим

'abcdef'[::2] -> 'ace'
'abcdef'[1::2] -> 'bdf'

Как вы видите, последний номер в срезе указывает интервал, который будет использоваться для сбора предметов. Вы можете узнать больше об использовании расширенных фрагментов здесь .

Функция zip берет первый элемент из первой итерации и объединяет ее с первым элементом со второй итерацией. Затем функция zip делает то же самое для второго и третьего элементов, пока у одного из итераций не заканчиваются значения.

Результатом является итератор. Если вы хотите список, используйте функцию list () для результата.

0 голосов
/ 13 февраля 2012
def chunks(iterable,n):
    """assumes n is an integer>0
    """
    iterable=iter(iterable)
    while True:
        result=[]
        for i in range(n):
            try:
                a=next(iterable)
            except StopIteration:
                break
            else:
                result.append(a)
        if result:
            yield result
        else:
            break

g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'
0 голосов
/ 16 декабря 2009

Вот как вы работаете со списком и итератором:

def isList(L): # Implement it somehow - returns True or false
...
return (list, lambda x:x)[int(islist(L))](result)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...