找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 157|回复: 0
打印 上一主题 下一主题

关于尾递归的使用详解

[复制链接]

2560

主题

2560

帖子

7622

积分

论坛元老

Rank: 8Rank: 8

积分
7622
跳转到指定楼层
楼主
发表于 2018-2-14 08:21:57 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

            这几天看到几篇关于尾递归的文章,之前对尾递归没有多大概念,所以回头研究了一下尾递归。

尾递归的概念
尾递归(Tail Recursion)的概念是递归概念的一个子集。对于普通的递归,由于必须要记住递归的调用堆栈,由此产生的耗用是难以估量的。比如下文中php小节第一个例子使用php写一个阶乘函数,就是由于递归造成了栈溢出的错误。尾递归出现的目的就是消除递归栈耗损这个缺憾的。
从代码层面看,尾递归其实一句话就可以说清楚了:
函数的最后一个操作是递归调用

比如"菲波纳锲"数列的php的递归实现:
[U]复制代码[/U] 代码如下:
fibonacci.php                                                                                                                          
这是递归函数,但不是尾递归,因为fibonacci的最后一个操作是加法操作。
转化为尾递归:
[U]复制代码[/U] 代码如下:
function fibonacci2($n, $acc1, $acc2) {
    if ($n == 0) {
        return $acc1;
    }   
    return fibonacci2($n-1, $acc2, $acc1 + $acc2);
}
fibonacci2就是一个尾递归,它增加两个累加器acc1和acc2,并给出初始的值。记住:递归转化为尾递归的思想一定是增加累加器,减少递归外操作。
尾递归在不同语言上的应用也是不同的。最常使用的就是函数式编程Erlang,几乎是所有出现递归的函数全部都修改成为尾递归。下面说一下尾递归在几个不同的语言上的表现和应用。
php中的尾递归
我们做个实验
普通递归:
[U]复制代码[/U] 代码如下:
尾递归:
[U]复制代码[/U] 代码如下:
实验结果:

事实证明,
尾递归在php中是没有任何优化效果的!
C中的尾递归
在C中的尾递归优化是gcc编译器做的。在gcc编译的时候加上-O2会对尾递归进行优化
我们可以直接看生成的汇编代码:
(使用gdb, gcc –O2 factorial.c –o factorial;    disass factorial)

未加-O2生成的汇编:

加了O2优化的汇编:

不要头大,我也是初看汇编,但是这份代码非常简单,去网上稍微搜搜命令,大致就能理解:
[U]复制代码[/U] 代码如下:
function factoral(n, sum) {
    while(n != 0){
        sum = n * sum
        n = n-1
    }
    return sum
}
gcc做的确实是智能优化。
如果你还有兴趣,你可以使用-O3对尾递归进行优化,并查看其中的汇编指令
-O3的优化是直接将循环展开
总结
一般的线性递归修改成为尾递归最大的优势在于减少了递归调用栈的开销。从php那个例子就明显看出来递归开销对程序的影响。但是并不是所有语言都支持尾递归的,即使支持尾递归的语言也一般是在编译阶段对尾递归进行优化,比如上例中的C语言对尾递归的优化。在使用尾递归对代码进行优化的时候,必须先了解这门语言对尾递归的支持。
            
            
您可能感兴趣的文章:
  • 尾递归详细总结分析
  • 使用python实现递归版汉诺塔示例(汉诺塔递归算法)
  • python实现斐波那契递归函数的方法
  • Lua中的函数(function)、可变参数、局部函数、尾递归优化等实例讲解
  • C#中的尾递归与Continuation详解
  • 使用70行Python代码实现一个递归下降解析器的教程
  • 讲解Python中的递归函数
  • python中尾递归用法实例详解
            
  • 分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    用户反馈
    客户端