使用While循环的Python尾递归“ Hack”

问题描述

我已经看到了一些通过使用while True循环使Python进行尾部调用优化的示例。例如

def tail_exp(base,exponent,acc=1):
    if exponent == 0:
        return acc
    return tail_exp(base,exponent - 1,acc * base)

成为


def tail_exp_2(base,acc=1):
    while True:
        if exponent == 0:
            return acc
        exponent,acc = exponent - 1,acc * base

我很好奇这种技术是否适用于Python中的所有/大多数递归算法,以及在以这种方式优化递归算法时是否存在任何缺点或“陷阱”?

解决方法

任何递归算法都可以用迭代算法代替。但是,某些示例将要求在代码中添加一个额外的堆栈,以管理由原始形式的递归调用处理的状态。使用尾部递归,不需要管理任何状态,因此不需要单独的堆栈。

一些编程语言利用这一事实,并设计其编译器以优化递归代码中的尾部调用,从而产生等效于循环的机器代码。 Python 不会进行尾部调用优化,因此这与您的问题无关。用手重写代码不是优化尾部调用,而只是一种特殊的重构。

Python选择不进行尾部调用优化的原因有很多。不是因为不可能。 Python代码被编译为字节代码,因此,至少在理论上,如果开发人员愿意,就有机会将递归调用转换为循环(实际上,这有点复杂,因为Python变量名是动态的,所以您不能必须告诉一个函数名称是否指向您在运行时期望的名称,例如 monkeypatching 之类的技术使用的事实。但是,尾部调用优化的最大问题是,它通常会覆盖通常由调用堆栈保留的有用的调试信息,例如确切的递归深度以及这些先前函数调用的确切状态。 Python开发人员已经决定,即使有可能实现尾部调用优化,他们也喜欢普通递归的简单性和可调试性,而不是尾部调用优化的性能优势。

如果要将算法从递归实现重写为迭代实现,则始终可以这样做。但是,在某些情况下,它可能变得更加复杂。即使迭代等效项可能更快(并且不会达到大输入的递归限制),某些算法的递归实现也可能更短,更简单并且更容易推理。将尾调用转换为循环通常很简单。复杂的情况通常也不适合进行尾部调用优化,因为它们使用递归返回的值来处理复杂的事情。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...