关于尾递归的使用详解


Posted in PHP onMay 02, 2013

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

尾递归的概念
尾递归(Tail Recursion)的概念是递归概念的一个子集。对于普通的递归,由于必须要记住递归的调用堆栈,由此产生的耗用是难以估量的。比如下文中php小节第一个例子使用php写一个阶乘函数,就是由于递归造成了栈溢出的错误。尾递归出现的目的就是消除递归栈耗损这个缺憾的。

从代码层面看,尾递归其实一句话就可以说清楚了:

函数的最后一个操作是递归调用
 

比如"菲波纳锲"数列的php的递归实现:

fibonacci.php                                                                                                                          
<?php 
function fibonacci($n) { 
    if ($n < 2) { 
        return $n;  
    }    
    return fibonacci($n - 1) + fibonacci($n - 2);  
}   
var_dump(fibonacci(30));

这是递归函数,但不是尾递归,因为fibonacci的最后一个操作是加法操作。

转化为尾递归:

function fibonacci2($n, $acc1, $acc2) { 
    if ($n == 0) { 
        return $acc1; 
    }    
    return fibonacci2($n-1, $acc2, $acc1 + $acc2); 
}

fibonacci2就是一个尾递归,它增加两个累加器acc1和acc2,并给出初始的值。记住:递归转化为尾递归的思想一定是增加累加器,减少递归外操作。
尾递归在不同语言上的应用也是不同的。最常使用的就是函数式编程Erlang,几乎是所有出现递归的函数全部都修改成为尾递归。下面说一下尾递归在几个不同的语言上的表现和应用。

php中的尾递归
我们做个实验

普通递归:

<?php 
function factorial($n) 
{ 
    if($n == 0) { 
        return 1; 
    }    
    return factorial($n-1) * $n;  
} var_dump(factorial(100000000));

尾递归:
<?php
function factorial($n, $acc)
{
    if($n == 0) {
        return $acc;
    }  
    return factorial($n-1, $acc * $n);
} 
var_dump(factorial(100000000, 1));

实验结果:

关于尾递归的使用详解

事实证明,
尾递归在php中是没有任何优化效果的!

C中的尾递归

在C中的尾递归优化是gcc编译器做的。在gcc编译的时候加上-O2会对尾递归进行优化

我们可以直接看生成的汇编代码:

(使用gdb, gcc ?O2 factorial.c ?o factorial;    disass factorial)
 

未加-O2生成的汇编:
关于尾递归的使用详解
加了O2优化的汇编:
关于尾递归的使用详解
不要头大,我也是初看汇编,但是这份代码非常简单,去网上稍微搜搜命令,大致就能理解:

function factoral(n, sum) {
    while(n != 0){
        sum = n * sum
        n = n-1
    }
    return sum}

gcc做的确实是智能优化。

如果你还有兴趣,你可以使用-O3对尾递归进行优化,并查看其中的汇编指令

-O3的优化是直接将循环展开

总结

一般的线性递归修改成为尾递归最大的优势在于减少了递归调用栈的开销。从php那个例子就明显看出来递归开销对程序的影响。但是并不是所有语言都支持尾递归的,即使支持尾递归的语言也一般是在编译阶段对尾递归进行优化,比如上例中的C语言对尾递归的优化。在使用尾递归对代码进行优化的时候,必须先了解这门语言对尾递归的支持。

PHP 相关文章推荐
smarty静态实验表明,网络上是错的~呵呵
Nov 25 PHP
PHP分页显示制作详细讲解
Dec 05 PHP
PHP语法速查表
Dec 06 PHP
Warning: session_destroy() : Trying to destroy uninitialized sessionq错误
Jun 16 PHP
PHP获取http请求的头信息实现步骤
Dec 16 PHP
php Calender(日历)代码分享
Jan 03 PHP
PHP中cookie和session的区别实例分析
Aug 28 PHP
php程序内部post数据的方法
Mar 31 PHP
php 无限分类 树形数据格式化代码
Oct 11 PHP
PHP关键特性之命名空间实例详解
May 06 PHP
搜索附近的人PHP实现代码
Feb 11 PHP
解决thinkphp6(tp6)在状态码500下不报错,或者显示错误“Malformed UTF-8 characters”的问题
Apr 01 PHP
基于Zend的Config机制的应用分析
May 02 #PHP
Zend的Registry机制的使用说明
May 02 #PHP
Zend的MVC机制使用分析(二)
May 02 #PHP
Zend的MVC机制使用分析(一)
May 02 #PHP
基于Zend的Captcha机制的应用
May 02 #PHP
PHP静态调用非静态方法的应用分析
May 02 #PHP
Mysql中分页查询的两个解决方法比较
May 02 #PHP
You might like
jquery获取ASP.NET服务器端控件dropdownlist和radiobuttonlist生成客户端HTML标签后的value和text值
2010/06/28 Javascript
基于jquery的划词搜索实现(备忘)
2010/09/14 Javascript
Bookmarklet实现启动jQuery(模仿 云输入法)
2010/09/15 Javascript
js 利用className得到对象的实现代码
2011/11/15 Javascript
日期处理的js库(迷你版)--自建js库总结
2011/11/21 Javascript
文本框回车提交与禁止提交示例
2013/09/27 Javascript
jquery重复提交请求的原因浅析
2014/05/23 Javascript
js实现iframe跨页面调用函数的方法
2014/12/13 Javascript
使用JavaScript获取地址栏参数的方法
2014/12/19 Javascript
jQuery中closest()函数用法实例
2015/01/07 Javascript
JavaScript每天定时更换皮肤样式的方法
2015/07/01 Javascript
高性能JavaScript DOM编程(1)
2015/08/11 Javascript
JS实现无限级网页折叠菜单(类似树形菜单)效果代码
2015/09/17 Javascript
Bootstrap表单组件教程详解
2016/04/26 Javascript
纯JS打造网页中checkbox和radio的美化效果
2016/10/13 Javascript
js鼠标跟随运动效果
2017/03/11 Javascript
js中let和var定义变量的区别
2018/02/08 Javascript
jQuery实现新闻播报滚动及淡入淡出效果示例
2018/03/23 jQuery
基于JavaScript实现单例模式
2019/10/30 Javascript
BootstrapValidator实现表单验证功能
2019/11/08 Javascript
解决VUE自定义拖拽指令时 onmouseup 与 click事件冲突问题
2020/07/24 Javascript
小程序实现简单语音聊天的示例代码
2020/07/24 Javascript
[03:40]DOTA2亚洲邀请赛小组赛第二日 赛事回顾
2015/01/31 DOTA
[50:21]Liquid vs Winstrike 2018国际邀请赛小组赛BO2 第二场
2018/08/19 DOTA
Python中字典的基础知识归纳小结
2015/08/19 Python
Python实现插入排序和选择排序的方法
2019/05/12 Python
解决django服务器重启端口被占用的问题
2019/07/26 Python
Python 实现顺序高斯消元法示例
2019/12/09 Python
Python搭建HTTP服务过程图解
2019/12/14 Python
秋季运动会表扬稿
2014/01/16 职场文书
超市优秀员工事迹材料
2014/05/01 职场文书
电子商务专业求职信范文
2015/03/19 职场文书
我的中国梦心得体会范文
2016/01/05 职场文书
HR在给员工开具离职证明时,需要注意哪些问题?
2019/07/03 职场文书
检讨书之工作不认真
2019/08/14 职场文书
详细介绍MySQL中limit和offset的用法
2022/05/06 MySQL