小编典典

在python中每n项拆分一个生成器/可迭代项(splitEvery)

python

我正在尝试用Python编写Haskel函数’splitEvery’。这是它的定义:

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))

此代码不适用于生成器表达式(感谢软糖修复该表达式):

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)

它确实提到了截断最后一个值的方法。这也不是我想要的。

保证了可迭代对象的从左到右的评估顺序。这使得使用izip(* [iter(s)] * n)将数据系列聚类为n个长度的组成为可能。

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

阅读 190

收藏
2020-12-20

共1个答案

小编典典

from itertools import islice

def split_every(n, iterable):
    i = iter(iterable)
    piece = list(islice(i, n))
    while piece:
        yield piece
        piece = list(islice(i, n))

一些测试:

>>> list(split_every(5, range(9)))
[[0, 1, 2, 3, 4], [5, 6, 7, 8]]

>>> list(split_every(3, (x**2 for x in range(20))))
[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81, 100, 121], [144, 169, 196], [225, 256, 289], [324, 361]]

>>> [''.join(s) for s in split_every(6, 'Hello world')]
['Hello ', 'world']

>>> list(split_every(100, []))
[]
2020-12-20