Я пытаюсь написать функцию 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]]