设为首页 收藏本站
查看: 1114|回复: 0

[经验分享] Python和Node.js支持尾递归吗?

[复制链接]

尚未签到

发表于 2015-4-21 10:42:23 | 显示全部楼层 |阅读模式
  什么是尾递归?简单来说就是最后返回的只是一个函数的调用,而不用保存多余的局部变量。
看一个简单的计算阶乘的例子(Lua代码):



function fact(n)
    return n==0 and 1 or n * fact(n-1)
end
  
  改成尾递归的方式就是:



function tail_fact(n, p)
    p = p or 1
    if n==0 then
        return p
    end
    return tail_fact(n-1, n*p)
end
  
  关于尾递归的更详细说明请参考:
http://en.wikipedia.org/wiki/Tail_call
  因为使用尾递归方式的时候,是不用保存局部变量的了,所以部分语言的解析器会对尾递归做优化,减少栈空间的占用。普通的递归方式则由于需要保存局部变量,栈空间则越占越多,最终导致栈溢出。
  Lua是支持尾递归的,所以上面的尾递归的代码,是可以很好的工作的。

Python的尾递归支持
  Python本身是不支持尾递归的(via),并且对递归次数有限制的,当递归次数超过1000次的时候,就会抛出“RuntimeError: maximum recursion depth exceeded”异常。
有人对此为Python的尾递归写了一个优化版本,让Python突破递归调用1000次的限制:Tail Call Optimization Decorator (Python recipe) ,或者你可以看看以下两篇文章:


  • 一个很Cool的Idear->Python的尾递归优化
  • 尾递归(Tail Recursion)
  以上的Python尾递归优化可以突破1000次的递归限制,但是却对于尾递归应有的优化完全没有。以下为我的测试代码:



#recursion.py
@tail_call_optimized
def fact(n):
    return 1 if n==0 else n * fact(n-1)
  



#tail_recursion.py
@tail_call_optimized
def tail_fact(n, p=1):
    if n==0:
        return p
    return tail_fact(n-1, n*p)
  测试环境是Python2.7.1,计算100000的阶乘。执行recursion.py的时候,内存占用约为100M,执行tail_recursion.py的时候,内存占用占到1G的时候,还是没有执行完,我只好杀掉进程。
  不过我确实想不明白为什么Python这里写成尾递归的方式会比会比普通方式占用多那么多内存呢?

Node.js对尾递归的支持
  我知道JavaScript是不支持尾递归的。ES4的时候曾经提过要加入尾递归的支持,不过后来被去掉了(via)。
周爱民的《Javascript语言精髓与编程实践》其中也提到:


  • “然而不幸的是。目前已知的javascript 的解释环境中并不支持这种特性(尾递归)。因此,我们在这里讨论函数室式时,可以说“能够通过函数递归来消灭循环语句”,但在不支持尾递归(及其优化技术)的javascript中,这种实现将以大量栈和内存消耗为代价”
  Node.js是基于Google V8的,所以在Chrome的控制台测试了一下:
DSC0000.png
  从结果看来,基本没戏了。但是还是在node.js写了代码验证一下。



// recursion.js
function fact(n){
    if(n == 0){
        console.log('fact: ');
        console.dir(process.memoryUsage() );
    }
    return n==0 ? 1 : n * fact(n-1);
}
  



// tail_recursion.js
function tailFact(n, p){
    p = p || 1;
    if(n==0){
        console.log('tail fact: ');
        console.dir(process.memoryUsage() );
        return p;
    }else{
        return tailFact(n-1, p*n);
    }
}
  
  以下为计算13000的阶乘时内存占用情况:



$ node recursion.js
fact:
{ rss: 7892992,
  vsize: 55169024,
  heapTotal: 2861216,
  heapUsed: 1634612 }
  



$ node tail_recursion.js
tail fact:
{ rss: 7901184,
  vsize: 55169024,
  heapTotal: 2869376,
  heapUsed: 1791520 }
  从结果来看,尾递归的方式占用的内存还要多。
  下面再来看看计算13000的阶乘,循环1000次,然后计算平均每次的执行时间:



var start = Date.now();
for(var i=0; i

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-59166-1-1.html 上篇帖子: 使用python多线程实现一个简单spider 下篇帖子: Python趣味编程(三)杨辉三角(原创)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表