Я пытаюсь написать функцию «splitEvery» Haskell в 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)]
Должен быть простой фрагмент кода, который выполняет разбиение. Я знаю, что у меня могут быть просто разные функции, но кажется, что это должно быть легко и просто. Я, вероятно, застрял на неважной проблеме, но это действительно беспокоит меня.
Он похож на группировщик из 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]]