我一直在研究 Python 中的函数式编程,并意识到有两种方法可以用递归替换循环。规范递归技术似乎不需要任何状态,例如下面的“factorial_canon”。另一种方法是使用状态变量来存储中间结果,例如下面的“factorial_alter”。
def factorial_canon(value):
if value == 1:
return 1
else:
return value*factorial_canon(value-1)
def factorial_alter(value, current = 1, state = 1):
if current <= value:
return factorial_alter(value, current + 1, state*current)
else:
return state
print 'factorial_canon: ', factorial_canon(5)
print 'factorial_alter: ', factorial_alter(5)
结果:
> factorial_canon: 120
> factorial_alter: 120
规范方法适用于小问题,但在实际项目中很快就会变得复杂。替代(基于状态)方法有什么缺点吗?它是否违反了纯函数实现的任何条件,例如,关于可变变量?
我意识到基于状态的方法无法从尾调用优化中获益,但我认为这无关紧要,因为 Python 无论如何都不支持此类优化。
请您参考如下方法:
不,恰恰相反:状态传递范例使 TCO 成为可能;简单递归的“规范”版本不是尾递归,因此 TCO 根本不适用于它。
尾调用优化的本质是堆栈帧变量(即参数)的重用:
fact 5:
fact_state 5 1 1:
fact_state 5 2 2:
fact_state 5 3 6:
fact_state 5 4 24:
fact_state 5 5 120:
return 120
简单递归版本必须在递归调用返回后执行乘法,因此必须将当前 n 压入堆栈,以便在递归调用返回时将其乘以返回值。
尾递归版本在递归调用之前相乘,因此返回后不需要做任何事情,不需要压栈,因此可以将最终值返回给系统(只有一个最终返回)。
这意味着递归尾部调用替换当前调用;它的参数存储在函数调用框架中,得到重用——就地更新(由编译器改变);在功能上将整个计算变成一个循环。毕竟,这是 TCO 的目标。
当然在Python中使用递归的方法就是不在Python中使用递归。正如您所说,Python 不提供 TCO。我们的程序仍然在其指令集 IOW 循环中具有程序计数器、跳转和可更新寄存器的计算机上运行。也许当智能尘埃到来时,这会改变。具有 TCO 的递归被转换为循环,这是将 TCO 放在首位的全部原因。
( see also )
