有没有Python中等价于Ruby的
在Ruby中,你可以这样做:
#each_cons
的功能?在Ruby中,你可以这样做:
array = [1,2,3,4]
array.each_cons(2).to_a
=> [[1,2],[2,3],[3,4]]
#each_cons
的功能?array = [1,2,3,4]
array.each_cons(2).to_a
=> [[1,2],[2,3],[3,4]]
我认为并没有一个现成的工具,我查看了内置模块itertools
,这也是我预期会有该功能的地方。不过你可以自己创建一个:
def each_cons(xs, n):
return [xs[i:i+n] for i in range(len(xs)-n+1)]
2
,但是在修改为任意 cons
后,它看起来和你的一样。 - Blenderx
是一个集合,所以最好使用 xs
(命名非常重要,即使在示例中也是如此。我甚至会说在示例中它更加重要 :))。 - tokland如果你需要这样的功能,itertools
就是你应该查看的模块:
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
那么:
>>> list(pairwise([1, 2, 3, 4]))
[(1, 2), (2, 3), (3, 4)]
对于更为通用的解决方案,可以考虑这个:
def split_subsequences(iterable, length=2, overlap=0):
it = iter(iterable)
results = list(itertools.islice(it, length))
while len(results) == length:
yield results
results = results[length - overlap:]
results.extend(itertools.islice(it, length - overlap))
if results:
yield results
这允许任意长度的子序列和任意重叠。用法:
>> list(split_subsequences([1, 2, 3, 4], length=2))
[[1, 2], [3, 4]]
>> list(split_subsequences([1, 2, 3, 4], length=2, overlap=1))
[[1, 2], [2, 3], [3, 4], [4]]
each_cons
那样表现(each_cons返回nil)。在snipsnipsnip的答案中的实现在这方面似乎更为适当。 - Elias Dorneleslist(split_subsequences([1, 2, 3, 4, 5, 6], length=3, overlap=1))
应该返回 [[1,2,3],[2,3,4],[3,4,5],[4,5,6]]
而不是 [[1, 2, 3], [3, 4, 5], [5, 6]]
。 - Eric Duminil我对列表的解决方案(Python2):
import itertools
def each_cons(xs, n):
return itertools.izip(*(xs[i:] for i in xrange(n)))
编辑: 在Python 3中,itertools.izip
已经不存在了,所以你需要使用普通的zip
函数:
def each_cons(xs, n):
return zip(*(xs[i:] for i in range(n)))
xs[i:]
的存在,它并不是真正的惰性。 - Elias Dornelesislice(xs, i, None)
而不是xs[i:]
。但出于以下原因,我更喜欢后者:a) 这个问题是关于列表的。b) 我大多数情况下都使用each_cons
来处理列表。c) 如果xs
是一个列表,切片后的列表将具有共享内存,因此可能比惰性处理更节省内存。 - snipsnipsnip#each_cons
适用于所有情况,所以我想指出这一点。我已经为那些需要的人发布了一个懒惰的解决方案。 - Elias Dornelesxrange()
。不过它在普通生成器上仍然失败了。这段小代码非常漂亮,再次感谢分享。 - Elias DornelesPython肯定可以做到这一点。如果您不想那么急切地做,可以使用itertool的islice和izip。另外,要记住,普通的切片会创建一个副本,所以如果内存使用很重要,您还应该考虑使用itertool的等效方法。
each_cons = lambda l: zip(l[:-1], l[1:])
更新:忽略下面我的回答,直接使用toolz.itertoolz.sliding_window()
即可,它会做正确的事情。
如果想要实现真正的懒加载,并且在序列/生成器长度不足时仍保留 Ruby 的 each_cons
行为,请使用 toolz.itertoolz.sliding_window()
。
import itertools
def each_cons(sequence, n):
return itertools.izip(*(itertools.islice(g, i, None)
for i, g in
enumerate(itertools.tee(sequence, n))))
示例:
>>> print(list(each_cons(xrange(5), 2)))
[(0, 1), (1, 2), (2, 3), (3, 4)]
>>> print(list(each_cons(xrange(5), 5)))
[(0, 1, 2, 3, 4)]
>>> print(list(each_cons(xrange(5), 6)))
[]
>>> print(list(each_cons((a for a in xrange(5)), 2)))
[(0, 1), (1, 2), (2, 3), (3, 4)]
n
的元组,结果是itertools.tee(xs, n)
(即“窗口大小”),而不是我们要迭代的序列。a = [1, 2, 3, 4]
out = [a[i:i + 2] for i in range(len(a) - 1)]
接近于@Blender的解决方案,但有一个修复:
最初的回答
a = [1, 2, 3, 4]
n = 2
out = [a[i:i + n] for i in range(len(a) - n + 1)]
# => [[1, 2], [2, 3], [3, 4]]
a = [1, 2, 3, 4]
n = 3
out = [a[i:i + n] for i in range(len(a) - n + 1)]
# => [[1, 2, 3], [2, 3, 4]]
from itertools import islice, tee
def each_cons(sequence, n):
return zip(
*(
islice(g, i, None)
for i, g in
enumerate(tee(sequence, n))
)
)
$ ipython
...
In [2]: a_list = [1, 2, 3, 4, 5]
In [3]: list(each_cons(a_list, 2))
Out[3]: [(1, 2), (2, 3), (3, 4), (4, 5)]
In [4]: list(each_cons(a_list, 3))
Out[4]: [(1, 2, 3), (2, 3, 4), (3, 4, 5)]
In [5]: list(each_cons(a_list, 5))
Out[5]: [(1, 2, 3, 4, 5)]
In [6]: list(each_cons(a_list, 6))
Out[6]: []
collections.deque
实现的例子。它支持任意生成器。from collections import deque
def each_cons(it, n):
# convert it to an iterator
it = iter(it)
# insert first n items to a list first
deq = deque()
for _ in range(n):
try:
deq.append(next(it))
except StopIteration:
for _ in range(n - len(deq)):
deq.append(None)
yield tuple(deq)
return
yield tuple(deq)
# main loop
while True:
try:
val = next(it)
except StopIteration:
return
deq.popleft()
deq.append(val)
yield tuple(deq)
使用方法:
list(each_cons([1,2,3,4], 2))
# => [(1, 2), (2, 3), (3, 4)]
# This supports generators
list(each_cons(range(5), 2))
# => [(0, 1), (1, 2), (2, 3), (3, 4)]
list(each_cons([1,2,3,4], 10))
# => [(1, 2, 3, 4, None, None, None, None, None, None)]
toolz.itertoolz.sliding_window()
。 - Elias Dorneles