【问题标题】:Python Equivalent to Ruby's #each_cons?Python 等价于 Ruby 的 #each_cons?
【发布时间】:2011-08-18 05:11:03
【问题描述】:

是否有与 Ruby 的 #each_cons 等效的 Pythonic 语言?

在 Ruby 中你可以这样做:

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

【问题讨论】:

  • 您为什么需要这样做?只是想知道;)
  • 我正在对列表进行移动平均。 #each_cons 是我在 Ruby 中的做法,所以我想知道 Pythonistas 是如何做到的。
  • 要真正等效于 Ruby 的 each_cons,请使用 toolz.itertoolz.sliding_window()

标签: python ruby enumerable equivalent


【解决方案1】:

我认为没有,我查看了内置模块itertools,这是我所期望的。不过,您可以简单地创建一个:

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

【讨论】:

  • +1 表示通用答案。我的为2 工作,但在将其修改为任意cons 后,它看起来像你的。
  • 一般来说,也适用于可迭代的解决方案是可取的,但是对于序列来说没关系。挑剔:x 是一个集合,所以更好xs(命名非常重要,即使在示例中也是如此。我什至会说它在示例中重要:))。
【解决方案2】:

对于此类事情,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 返回 nil)时,这里的一般解决方案不会像 each_cons 那样表现。在这方面,snipsnipsnip 的答案中的实现似乎更合适。
  • list(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]]
【解决方案3】:

我的列表解决方案(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 是生成器,则此解决方案不起作用,而且由于切片 xs[i:],它并不是真的懒惰。
  • 你说得对,我可以写islice(xs, i, None) 而不是xs[i:]。出于某种原因,我更喜欢后者:a)问题是关于列表的。 b)I use each_cons for lists most of the time。 c) 如果xs 是一个列表,则切片列表将具有共享内存,因此它可能比懒惰更节省内存。
  • 是的,你是对的,我知道。 =) 只是 Ruby 的 #each_cons 适用于一切,所以我想我应该指出它。我已经为需要的人发布了一个懒惰的解决方案。
  • 我暂时删除了我的答案,看到你添加 islice 的方法与xrange() 一起工作。但是,它仍然无法使用普通的生成器。这段小代码很漂亮,再次感谢分享。
【解决方案4】:

快速单行:

a = [1, 2, 3, 4]

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

【讨论】:

    【解决方案5】:

    Python 肯定可以做到这一点。如果你不想那么急切,使用itertool 的islice 和izip。此外,重要的是要记住普通切片会创建一个副本,因此如果内存使用很重要,您还应该考虑 itertool 等价物。

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

    【讨论】:

      【解决方案6】:

      更新:别在意我在下面的回答,只需使用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 的参数上使用的元组解包应用于大小为 n 的元组itertools.tee(xs, n)(即“窗口大小”),而不是我们想要迭代的序列。

      【讨论】:

        【解决方案7】:

        接近@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]]
        

        【讨论】:

          【解决方案8】:

          Python 3 版本 Elias 的代码

          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]: []
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-08-15
            • 2012-11-26
            • 1970-01-01
            • 2019-03-14
            • 2017-02-06
            • 1970-01-01
            • 2010-09-23
            • 2012-02-21
            相关资源
            最近更新 更多