Python尾部递归;“黑客”;使用While循环

Python尾部递归;“黑客”;使用While循环,python,recursion,tail-recursion,Python,Recursion,Tail Recursion,我已经看到了一些例子,通过使用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, exponent, acc=1): while True: if exp

我已经看到了一些例子,通过使用
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, exponent, acc=1):
    while True:
        if exponent == 0:
            return acc
        exponent, acc = exponent - 1, acc * base

我很想知道这种技术是否适用于Python中的所有/大多数递归算法,以及在以这种方式优化递归算法时是否有任何缺点或“缺陷”需要注意?

任何递归算法都可以被迭代算法所取代。但是,有些示例需要在代码中添加额外的堆栈,以管理由原始形式的递归调用处理的状态。对于尾部递归,没有要管理的状态,因此不需要单独的堆栈

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

Python选择不进行尾部调用优化有几个原因。这并不是因为不可能。Python代码被编译成字节码,因此至少在理论上,如果开发人员需要的话,可以将递归调用转换成循环(实际上要复杂一点,因为Python变量名是动态的,所以您不一定知道函数名是否引用了您在运行时希望它引用的内容,这是monkeypatching等技术使用的事实)。但是,尾部调用优化的最大问题是,它通常会覆盖调用堆栈通常会保留的有用调试信息,例如递归的深度以及之前函数调用的确切状态。Python开发人员决定,他们更喜欢简单和调试正常递归对尾部调用优化性能好处的敏感性,即使后者是可能的


如果您想将算法从递归实现重写为迭代实现,您总是可以这样做的。但是,在某些情况下,它可能会变得更复杂。某些算法的递归实现可能会更短、更简单,并且更容易推理,即使迭代等价物可能更快(并且不会达到大输入的递归限制)但是,将尾部调用转换为循环通常非常简单。复杂的情况通常也不适合尾部调用优化,因为它们使用递归返回的值执行复杂的操作。

这就是尾部调用优化通常转换为命令式代码的方式,是的。严格地说,
tail_exp_2
不使用递归…因此没有真正的尾部调用优化。@HiroProgator我倾向于同意,但我看到这种方法被吹捧为在Python中进行尾部调用优化的一种方法。因此,如果这不是递归,它到底是什么,它与递归的关系是什么?这既不是特定于Python的,也不是特定于Python的“尾部调用优化”--您只需手动将递归算法转换为迭代算法,通常您都可以这样做。TCO是编译器自动为您进行转换时您所称的,因为在尾部递归的特定情况下,转换相当简单。没有“陷阱”“只要您正确地进行转换,它就会参与。这是一个循环-一般来说,递归需要在循环中建立一个调用堆栈,在返回时释放。在TCO的情况下,堆栈的中间元素无关紧要,您只需覆盖变量即可。