小白python进击第二周2.尾递归

来源:互联网 发布:手机淘宝不出菜鸟驿站 编辑:程序博客网 时间:2024/05/18 02:55
递归包含两个方面的内容,一个是递归的计算过程,一个是递归过程,后者是语法上的事实而前者是概念上的计算过程,事实上在程序上我们也许是使用循环来实现的。
Python本身是不支持尾递归的(via),并且对递归次数有限制的,当递归次数超过1000次的时候,就会抛出“RuntimeError: maximum recursion depth exceeded”异常。
尾递归和一般递归不同对内存的占用
普通的递归创建stack累积而后计算收缩由于需要保存局部变量,栈空间则越占越多,最终导致栈溢出。
尾递归只会占用恒量的内存(和迭代一样)迭代:http://developer.51cto.com/art/201603/507935.htm
这个就是一个简单的递归函数

函数过程

===> fact(5)===> 5 * fact(4)===> 5 * (4 * fact(3))===> 5 * (4 * (3 * fact(2)))===> 5 * (4 * (3 * (2 * fact(1))))===> 5 * (4 * (3 * (2 * 1)))===> 5 * (4 * (3 * 2))===> 5 * (4 * 6)===> 5 * 24===> 120
这个曲线就代表内存占用大小的峰值,从左到右,达到顶峰,再从右到左收缩。而我们通常不希望这样的事情发生,所以使用迭代,只占据常量stack space(更新这个栈!而非扩展他)
尾递归:


理论对应调用如下:
===> fact_iter(5, 1)===> fact_iter(4, 5)===> fact_iter(3, 20)===> fact_iter(2, 60)===> fact_iter(1, 120)===> 120
从上面可以知道,fact_iter(x,y)中形式变量y的实际变量值是不断更新的,与普通递归对比可以看出,fact_iter(x,y)中y值不变只是在层次上加深。尾递归是把变化的参数传递给递归函数的变量了
形式上只要最后一个return语句是单纯函数就可以写成尾递
 return  fact_iter(num-1,num*product)

尾递归的判断标准是函数运行最后一步是否调用自身,而不是是否在函数的最后一行调用自身。
好处:因为进入最后一步后不再需要参考外层函数(caller)的信息,因此没必要保存外层函数的stack,递归需要用的stack只有目前这层函数的,因此避免了栈溢出风险。
0 0
原创粉丝点击