我正在尝试计算列表的长度。当我在 cmd 上运行它时,我得到:
RuntimeError:比较超过最大递归深度
我认为我的代码没有任何问题:
def len_recursive(list):
if list == []:
return 0
else:
return 1 + len_recursive(list[1:])
不要使用递归,除非你可以预测它不会太深。Python 对递归深度的限制非常小。
如果你坚持递归,有效的方法是:
def len_recursive(lst):
if not lst:
return 0
return 1 + len_recursive(lst[1::2]) + len_recursive(lst[2::2])
Python 中的递归深度是有限的,但可以增加,如本文所示。如果 Python 支持尾调用优化,则此解决方案适用于任意长度的列表:
def len_recursive(lst):
def loop(lst, acc):
if not lst:
return acc
return loop(lst[1:], acc + 1)
return loop(lst, 0)
但事实上,您将不得不使用较短的列表和/或增加允许的最大递归深度。
当然,没有人会在现实生活中使用这个实现(而不是使用len()
内置函数),我猜这是递归的学术示例,但即便如此,这里最好的方法是使用迭代,如图所示@poke 的回答。
正如其他人所解释的,您的功能存在两个问题:
sys.getrecursionlimit
.第一个很容易解决。例如,请参阅 Óscar López 的回答。
第二个很难解决,但并非不可能。一种方法是使用协程(基于生成器)而不是子例程。另一种方法是不实际递归调用函数,而是返回具有递归结果的函数,并使用应用结果的驱动程序。有关如何实现后者的示例,请参阅Paul Butler在 Python 中的尾递归,但这就是您的情况。
从 Paul Butler 的tail_rec
函数开始:
def tail_rec(fun):
def tail(fun):
a = fun
while callable(a):
a = a()
return a
return (lambda x: tail(fun(x)))
这不能作为他的案例的装饰器,因为他有两个相互递归的函数。但在你的情况下,这不是问题。因此,使用 Óscar López 的版本:
@tail_rec
def tail_len(lst):
def loop(lst, acc):
if not lst:
return acc
return lambda: loop(lst[1:], acc + 1)
return lambda: loop(lst, 0)
现在:
>>> print tail_len(range(10000))
10000
多田。
如果您真的想使用它,您可能想要制作tail_rec
一个更好的装饰器:
def tail_rec(fun):
def tail(fun):
a = fun
while callable(a):
a = a()
return a
return functools.update_wrapper(lambda x: tail(fun(x)), fun)
您的异常消息意味着您的方法被递归调用太频繁,因此您的列表可能太长而无法像这样递归地计算元素。您可以简单地使用迭代解决方案来做到这一点:
def len_iterative(lst):
length = 0
while lst:
length += 1
lst = lst[1:]
return length
请注意,这很可能仍然是一个糟糕的解决方案,因为lst[1:]
会继续创建列表的副本。所以你最终会得到len(lst) + 1
列表实例(长度0
为len(lst)
)。直接使用内置可能是最好的主意len
,但我想这是一项任务。
想象一下,您正在使用一叠纸来运行它。你想计算你有多少张纸。如果有人给你 10 张纸,你拿第一张纸,把它放在桌子上,抓起下一张纸,放在第一张纸旁边。你这样做了 10 次,你的桌子已经满了,但你已经把每张纸都摆好了。然后你开始计算每一页,在你计数时回收它,0 + 1 + 1 + ... => 10。这不是计算页面的最佳方法,但它反映了递归方法和 python 的实现。
这适用于少量页面。现在想象有人给了你 10000 张纸。很快,您的办公桌上就没有空间来布置每一页了。这基本上就是错误消息告诉您的内容。
最大递归深度是表格可以容纳的“多少张”。每次调用 python 时都需要保留“1 + 递归调用的结果”,以便在所有页面都布置好后,它可以返回并计算它们。不幸的是,在最后的计数发生之前你的空间已经用完了。
如果你想递归地学习,因为你想在任何合理的情况下使用 len() ,只需使用小列表,25 应该没问题。
如果某些系统支持尾调用,则它们可以处理大型列表
Python 没有优化尾递归调用,因此使用这种递归算法不是一个好主意。您可以使用sys.setrecursionlimit()调整堆栈,但这仍然不是一个好主意。