【问题标题】:Proper lists and recursive tail in PythonPython中的正确列表和递归尾部
【发布时间】:2012-10-02 17:33:18
【问题描述】:

在各种 Lisps 中,proper list 要么是 nil(空值)要么是 cons 单元格,其中第一个(head,first,car)值指向一个值,第二个(tail,rest, cdr) 指向另一个正确的列表。其他各种函数式编程语言都实现了这种头尾功能,包括 Erlang 和 Scala。在 Common Lisp 和 Emacs Lisp 中,您可以无限递归地找到列表的尾部:

(rest (rest (rest (rest (rest (rest ()))))))

它将产生nil。我想在 Python 中模拟这种行为。当然,为了性能,我最好坚持使用经过大量优化的本机数据类型,所以这只是为了练习。我的代码是:

class MyList:
    def __init__(self, *xs):
        self._x = []
        self._x.extend(xs)
        self.is_empty = not xs
        self.head = xs[0] if xs else None
        self.tail = MyList(*xs[1:]) if xs[1:] else MyList([])

但是,现在调用 tail 会进入递归并导致最大递归深度错误。我怎样才能使下面的表达式成为可能?换句话说,如何在 Python 中创建适当列表的功能?

a = MyList(1,2)
my_list.tail.tail.tail.tail.tail.tail

相关问题,但没有回答我的问题:LISP cons in python

【问题讨论】:

    标签: python cons cdr


    【解决方案1】:

    我已尝试稍微重写您的示例 - 这似乎对我有用而不会破坏堆栈。

    class MyList(object):
        def __init__(self, *xs):
            self._x = xs if all(xs) else tuple()
            self.head = xs[0] if xs else None
    
        @property
        def is_empty(self):
            return not self._x
    
        @property
        def tail(self):
            return MyList(self._x[1:]) if self._x[1:] else MyList([])
    
    s = MyList(1, 2)
    print s.tail.tail.tail.tail.tail.tail
    

    【讨论】:

      【解决方案2】:

      与其尝试创建类并将其绑定到列表,不如编写自己的链表(这基本上是 lisps 的工作方式,包含一个元素的节点链和下一个节点(代表其余的列表)。

      我的蟒蛇有点生锈,但我会尝试一下。考虑这个伪代码:

      class WugList:
          def __init__(self, *xs):
              self.head = xs[0] if (len(xs) > 0) else None
              self.tail = WugList(xs[1:]) if (len(xs) > 0) else self
              self.is_empty = (len(xs) > 0)
      
          def car(self):
              return self.head
      
          def cdr(self):
              return self.tail
      

      然后您应该能够使用以下内容:

      derp = WugList([1,2,3,42,69,9001])
      a = derp.car()                   # 1
      b = derp.cdr().car()             # 2
      c = derp.cdr().cdr().cdr().car() # 42
      
      # chaining cdr infinitely is possible because the last node's head is None
      # and its tail is itself
      d = derp.cdr().cdr().cdr().cdr().cdr().cdr().cdr().cdr().cdr().cdr()
                    .cdr().cdr().cdr().cdr().cdr().cdr().cdr().cdr().cdr()
                    .cdr().cdr().cdr().cdr().cdr().cdr().cdr().cdr().car() # None
      

      【讨论】:

      • 请注意,这不会修改或保存作为参数传递给它的列表,它会在创建节点时从中读取元素,然后忘记它。
      【解决方案3】:

      如果您想无限地获取列表的tail 属性,则需要使用property。这样,在您请求之前不会评估尾部,从而防止无限递归。

      class MyList:
          def __init__(self, *xs):
              self._x = []
              self._x.extend(xs)
              self.is_empty = not xs
              self.head = xs[0] if xs else None
      
          @property
          def tail(self):
              return MyList(*self._x[1:])
      
      a = MyList(1,2)
      print a._x
      # [1, 2]
      print a.tail._x
      # [2]
      print a.tail.tail._x
      # []
      print a.tail.tail.tail._x
      # []
      print a.tail.tail.tail.tail._x
      # []
      

      【讨论】:

      • 为什么tail中需要if/else?
      • 这个例子的运行空间不是很大吗? Asker 的示例也是如此,因为它为每个节点复制了整个列表(一次修剪一个元素。对于 x 个节点的列表,通过构造 MyList 创建的列表的总大小将是 x*x/ 2.
      • @Wug:不,我的运行空间不大,无法构建MyList(尽管提问者有),因为tail 仅在您请求时计算。执行a.tail.tail.tail.tail... 确实有一个与.tails 的数量成比例的运行空间,但无论您的实现是什么,这都是正确的(因为这要求每个.tail 成为它自己的列表)。
      • 是的,但我相信它仍然适用于 N^2;当你调用tail时,你创建了一个新的MyList,它复制了整个存储的列表(第一个元素除外)。 list1.extend(list2) 将 list2 中的所有元素添加到 list1 但不修改 list2。
      猜你喜欢
      • 2015-08-10
      • 1970-01-01
      • 1970-01-01
      • 2013-03-21
      • 1970-01-01
      • 2015-01-18
      • 2011-07-01
      • 1970-01-01
      • 2011-11-14
      相关资源
      最近更新 更多