2011-05-04 100 views
9

Pythonic是否與Ruby的#each_cons相同?Python等效於Ruby的#each_cons?

在Ruby中,你可以這樣做:

array = [1,2,3,4] 
array.each_cons(2).to_a 
=> [[1,2],[2,3],[3,4]] 
+0

爲什麼你需要做到這一點?只是想知道;) – Blender 2011-05-04 04:12:37

+0

我正在做一個列表的移動平均線。 #each_cons是我在Ruby中如何做的,所以我想知道Pythonistas是如何做到的。 – maxhawkins 2011-05-04 04:38:08

+0

對於真正相當於Ruby的each_cons,請使用['toolz.itertoolz.sliding_window()'](http://toolz.readthedocs.org/en/latest/_modules/toolz/itertoolz.html#sliding_window)。 – elias 2015-04-19 00:03:32

回答

10

對於這樣的事情,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]] 
+0

的Python 3:http://docs.python.org/py3k/library/itertools.html - 的Py2:http://docs.python.org/library/itertools.html – 2011-05-04 05:22:24

+0

這裏的一般解決方案不會表現得像'each_cons '當你有一個長度不足的序列時(each_cons返回nil)。在snipsnipsnip的答案中的實現似乎更適合於這方面。 – elias 2014-01-11 21:47:28

2

一個快速班輪:

a = [1, 2, 3, 4] 

out = [a[i:i + 2] for i in range(len(a) - 1)] 
14

我不認爲這是一個,我通過看內置模塊itertools,這是我希望它是。你可以簡單地創建一個:

def each_cons(x, size): 
    return [x[i:i+size] for i in range(len(x)-size+1)] 
+1

+1通用答案。我爲'2'工作,但在修改它爲任意'cons'後,它看起來就像你的。 – Blender 2011-05-04 04:22:08

+0

在也工作了iterables一般的解決方案是preferrable,但遠,這是確定的序列。吹毛求疵:'x'是一個集合,所以最好'xs'(命名是很重要的,即使是在實例我甚至會說這是* *以上例子中重要的:))。 – tokland 2014-03-07 09:19:30

3

Python肯定可以做到這一點。如果你不想這麼熱切地使用itertool的islice和izip。此外,重要的是要記住正常的切片將創建一個副本,所以如果內存使用很重要,你還應該考慮itertool等價物。

each_cons = lambda l: zip(l[:-1], l[1:])

4

我對名單的解決方案:

import itertools 
def each_cons(xs, n): 
    return itertools.izip(*(xs[i:] for i in xrange(n))) 
+0

很好的解決方案,謝謝! =) – elias 2013-12-16 17:00:16

+0

如果參數xs是一個生成器,則此解決方案不起作用,而且由於slice'xs [i:]',它不是非常懶惰。 – elias 2014-01-12 14:02:10

+0

你說得對,我可以寫'islice(xs,i,None)'而不是'xs [i:]'。由於某種原因,我更喜歡後者:a)問題與列表有關。 B)[I用於列表大部分時間'each_cons'](http://c2.com/cgi/wiki?PrematureGeneralization)。 c)如'xs'是一個列表,切片列表將具有共享存儲器,所以它可能是存儲器比做得懶高效。 – snipsnipsnip 2014-01-12 17:50:57

2

UPDATE:沒關係我的回答如下,只是用toolz.itertoolz.sliding_window() - 它會做正確的事情。


對於一個真正的懶惰實現,它保留了Ruby的each_cons的行爲時,順序/發電機具有足夠的長度:

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

注意拆包使用的元組對於izip的參數應用於由itertools.tee(xs, n)(即「窗口大小」)產生的大小爲n的元組,而不是我們想要迭代的序列。

0

同埃利亞斯的代碼,但適用於Python 2和3:

try: 
    from itertools import izip # python 2 
except ImportError: 
    from builtins import zip as izip # python 3 

from itertools import islice, tee 

def each_cons(sequence, n): 
    return izip(
     *(
      islice(g, i, None) 
      for i, g in 
      enumerate(tee(sequence, n)) 
     ) 
    )