【问题标题】:Python equivalent of maplist?Python相当于maplist?
【发布时间】:2010-10-29 08:29:56
【问题描述】:

Common Lisp 的 maplist 函数的最佳 Python 等效项是什么?来自maplist documentation

maplist 和 mapcar 一样,除了 函数应用于连续 列表的子列表。功能是 首先应用于列表本身, 然后到每个列表的 cdr,和 然后到每个 cdr 的 cdr 列表等等。

示例(伪代码,未经测试):

>>> def p(x): return x
>>> maplist(p, [1,2,3])
[[1, 2, 3], [2, 3], [3]]

注意:在上例中传递给p 的参数将是列表[1, 2, 3][2, 3][3];即,p 不适用于这些列表的元素。例如:

>>> maplist(lambda l: list(reversed(l)), [1,2,3])
[[3, 2, 1], [3, 2], [3]]

【问题讨论】:

  • 如果您能展示输入和预期输出,它可能会更有帮助
  • @SilentGhost +1,我不知道 maplist 在三读时应该做什么。
  • 好的,添加示例,感谢您的建议。
  • 呃,我的例子糟透了。添加了一个更好的(希望如此)。

标签: python functional-programming lisp


【解决方案1】:

Python 有一个链表类型,称为deque

from collections import deque

def maplist(f, lst):
    linked = deque(lst)
    results = []
    while linked:
        results.append(f(linked))
        linked.popleft() # assumes left is head, as in lisp

    return results

In [134]: maplist(lambda l: list(reversed(l))*2, [1,2,3])
Out[134]: [[3, 2, 1, 3, 2, 1], [3, 2, 3, 2], [3, 3]]

线性时间,做同样的事情,将具有与对 lisp 列表的操作相同的性能特征(即修改内部元素将在被修改列表的长度上是线性的),以及相同的语义(即如果 f修改列表,它会为 maplist 调用内的所有后续执行 f 修改它;但是,因为这会创建 lst 的副本,f 不能修改 lst,这与 Common Lisp 不同。

【讨论】:

  • deque 是使用双向链表实现的,但它不是 LL 本身,例如,您将如何引用它的单独部分?
  • @J.F.Sebastian 公平地说,你不能,除了数字索引。
【解决方案2】:

你可以为此编写一个小函数

def maplist(func, values):
    return [map(func, values[i:]) for i in xrange(len(values))]

>>> maplist(lambda a: a* 2, [1,2,3])
[[2, 4, 6], [4, 6], [6]]

[编辑]

如果您想在子列表上应用该函数,您可以将函数更改为:

def maplist(func, values):
    return [func(values[i:]) for i in xrange(len(values))]

>>> maplist(lambda l: list(reversed(l)), [1,2,3])
[[3, 2, 1], [3, 2], [3]]

【讨论】:

  • 你不能对迭代器进行切片。所以第二个参数名字不好。
  • 不幸的是,我可能在我的问题中选择了一个模棱两可的例子; 'maplist' 应该将 'func' 直接应用于子列表 [1,2,3]、[2,3]、[3],而不是这些子列表的元素。
  • @Jacob,检查编辑,我添加了另一个功能,可以满足您的需求。
  • -1 因为该功能不等效。 LISP 的 maplist 是一种线性时间算法。
  • 几件事:这似乎根本不像 maplist 所做的那样。我见过的例子都显示了多个列表,输出的大小等于最小列表的大小。例如:maplist(lambda a: a*2, [[1,2,3], [1,2]]) -> [ [[2,4,6], [2,4]], [[2 ,4], [2]] ].
【解决方案3】:

O(N) 对链表的 maplist() 实现

maplist = lambda f, lst: cons(f(lst), maplist(f, cdr(lst))) if lst else lst

请参阅Python Linked List 问题。

【讨论】:

    【解决方案4】:

    正如@Cybis 和其他人所提到的,Python 列表无法保持 O(N) 复杂度;您必须创建一个链接列表。冒着证明Greenspun's 10th rule的风险,这里有这样一个解决方案:

    class cons(tuple):
        __slots__=()
    
        def __new__(cls, car, cdr):
            return tuple.__new__(cls, (car,cdr))
    
        @classmethod
        def from_seq(class_, l):
            result = None
            for el in reversed(l):
                result = cons(el, result)
            return result
    
        @property
        def car(self): return self._getitem(0)
    
        @property
        def cdr(self): return self._getitem(1)
    
        def _getitem(self, i):
            return tuple.__getitem__(self, i)
    
        def __repr__(self):
            return '(%s %r)' % (self.car, self.cdr)
    
        def __iter__(self):
            v = self
            while v is not None:
                yield v.car
                v = v.cdr
    
        def __len__(self):
            return sum(1 for x in self)
    
        def __getitem__(self, i):
            v = self
            while i > 0:
                v = v.cdr
                i -= 1
            return v.car
    
    def maplist(func, values):
        result = [ ]
        while values is not None:
            result.append(func(values))
            values = values.cdr
        return result
    

    测试结果:

    >>> l = cons.from_seq([1,2,3,4])
    >>> print l
    (1 (2 (3 (4 None))))
    >>> print list(l)
    [1, 2, 3, 4]
    >>> print maplistr(lambda l: list(reversed(l)), cons.from_seq([1,2,3]))
    [[3, 2, 1], [3, 2], [3]]
    

    编辑:这是一个基于生成器的解决方案,它基本上解决了相同的问题,而无需使用链表:

    import itertools
    
    def mapiter(func, iter_):
        while True:
            iter_, iter2 = itertools.tee(iter_)
            iter_.next()
            yield func(iter2)
    

    测试结果:

    >>> print list(mapiter(lambda l: list(reversed(list(l))), [1,2,3]))
    [[3, 2, 1], [3, 2], [3]]
    

    【讨论】:

    • +1 表明即使是 Python 也无法逃脱 Greenspun 的第 10 条规则。
    • 您的生成器解决方案在大列表中出现此错误:RuntimeError: maximum recursion depth exceeded
    • 在 100 个条目的列表上运行 1000 次时,比常规列表理解解决方案慢 7 倍。
    • @Rick,我测试了您的两种解决方案,即使对于大型列表,它们都非常慢。第一个比第二个慢得多。第二个比常规列表理解解决方案慢 7 倍。
    • @Nadia,感谢 cmets。我希望这两种解决方案都相当慢(并且希望 mapiter() 解决方案有递归深度问题。)这个答案的目标只是复制 maplist 的 O(N) 缩放,不一定让它更快总体而言。
    【解决方案5】:

    Eewww... 对列表进行切片是一种线性时间操作。迄今为止发布的所有解决方案都具有 O(n^2) 时间复杂度。我相信 Lisp 的 maplist 有 O(n)。

    问题是,Python 的列表类型不是链表。它是一个可动态调整大小的数组(即,像 C++ STL 的“向量”类型)。

    如果保持线性时间复杂度对您很重要,则不可能在 Python 列表上创建“maplist”函数。最好修改代码以使用列表中的索引,或者将列表转换为实际的链表(仍然是线性时间操作,但会产生很多开销)。

    【讨论】:

    • 这是一个很好的观点。在我认为列表足够短的情况下,这不会是一个问题。但一般情况下可以。也许涉及生成器的东西也可以工作?
    • 这是个好主意 - 如果 maplist 接受一个函数,该函数本身接受生成器而不是列表。实现会有点复杂,而且开销会比 LISP 的 maplist 更多(来自创建所有这些生成器),但我认为它可以工作。
    • 我在下面编辑了我的答案(希望很快就在上面)以添加生成器解决方案
    • Python 有一个链表类型,叫做双端队列。我已经使用它添加了一个线性解决方案的答案。
    • 这里是linear time algorithm(虽然不实用)
    【解决方案6】:

    您可以使用嵌套列表推导:

    >>> def p(x): return x
    >>> l = range(4)[1:]
    >>> [p([i:]) for i in range(len(l))]
    [[1, 2, 3], [2, 3], [3]]
    

    您可以使用它自己定义地图列表:

    >>> def maplist(p, l): return [p([i:]) for i in range(len(l))]
    >>> maplist(p, l)
    [[1, 2, 3], [2, 3], [3]]
    

    【讨论】:

    • 我可能选择了一个模棱两可的例子,只使用了标识函数;函数“p”应该只传递子列表本身,而不是子列表的元素。我对问题进行了澄清。
    • @Jacob 这样更容易:)
    • 我想知道您如何测试您提出的解决方案以提供您列出的结果。当我尝试复制您输入的内容时,“p([i:])”在您的回复中给了我两个地方的语法错误。
    【解决方案7】:

    修改亚伦的回答:

    In [8]: def maplist(p, l): return [p([x for x in l[i:]]) for i in range(len(l))]
       ...: 
    
    In [9]: maplist(lambda x: x + x, [1,2,3])
    Out[9]: [[1, 2, 3, 1, 2, 3], [2, 3, 2, 3], [3, 3]]
    

    【讨论】:

      【解决方案8】:

      这就像你的例子(我已经修改了 reyjavikvi 的代码)

      def maplist(func, l):
          result=[]
          for i in range(len(l)):
              result.append(func(l[i:]))
          return result
      

      【讨论】:

        【解决方案9】:

        我认为没有,但以下功能可以工作:

        def maplist(func, l):
            for i in range(len(l)):
                func(l[i:])
        

        【讨论】:

        • 这个解决方案的一个问题是 maplist 会提供对 func 的所有调用结果的列表,而这个解决方案没有这样做。
        猜你喜欢
        • 2011-06-25
        • 2021-10-13
        • 2019-01-08
        • 2014-03-06
        • 2012-12-03
        • 2016-06-23
        • 2011-05-29
        • 2011-12-12
        • 2016-05-08
        相关资源
        最近更新 更多