关于尾递归的使用详解


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 相关文章推荐
PHP 反射机制实现动态代理的代码
Oct 22 PHP
php 破解防盗链图片函数
Dec 09 PHP
PHP 采集程序 常用函数
Dec 18 PHP
php动态实现表格跨行跨列实现代码
Nov 06 PHP
基于在生产环境中使用php性能测试工具xhprof的详解
Jun 03 PHP
php弹出对话框实现重定向代码
Jan 23 PHP
ThinkPHP学习笔记(一)ThinkPHP部署
Jun 22 PHP
PHP 验证登陆类分享
Mar 13 PHP
PHP使用mysqldump命令导出数据库
Apr 14 PHP
CI框架中$this-&gt;load-&gt;library()用法分析
May 18 PHP
基于thinkPHP类的插入数据库操作功能示例
Jan 06 PHP
PHP文件上传小程序 适合初学者学习!
May 23 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
YII Framework框架教程之安全方案详解
2016/03/14 PHP
JavaScript面象对象设计
2008/04/28 Javascript
JavaScript constructor和instanceof,JSOO中的一对欢喜冤家
2009/05/25 Javascript
JavaScript的变量作用域深入理解
2009/10/25 Javascript
javascript验证身份证完全方法具体实现
2013/11/18 Javascript
javascript处理表单示例(javascript提交表单)
2014/04/28 Javascript
JavaScript支持的最大递归调用次数分析
2014/06/24 Javascript
JavaScript时间操作之年月日星期级联操作
2016/01/15 Javascript
Google 地图事件实例讲解
2016/08/06 Javascript
Angular2-primeNG文件上传模块FileUpload使用详解
2017/01/14 Javascript
Angular中支持SCSS的方法
2017/11/18 Javascript
vue.js 实现点击按钮动态添加li的方法
2018/09/07 Javascript
详解如何在Node.js的httpServer中接收前端发送的arraybuffer数据
2018/11/11 Javascript
JavaScript 九种跨域方式实现原理
2019/02/11 Javascript
Python 3.x 连接数据库示例(pymysql 方式)
2017/01/19 Python
python实现unicode转中文及转换默认编码的方法
2017/04/29 Python
PYTHON基础-时间日期处理小结
2018/05/05 Python
利用pandas进行大文件计数处理的方法
2018/07/25 Python
浅谈Python中eval的强大与危害
2019/03/13 Python
python GUI图形化编程wxpython的使用
2019/07/19 Python
Series和DataFrame使用简单入门
2019/11/13 Python
三个python爬虫项目实例代码
2019/12/28 Python
巧用CSS3的calc()宽度计算做响应模式布局的方法
2018/03/22 HTML / CSS
ALEX AND ANI:手镯,项链,耳环和更多
2017/04/20 全球购物
ONLY德国官方在线商店:购买时尚女装
2017/09/21 全球购物
Paul Smith英国官网:英国国宝级时装品牌
2019/03/21 全球购物
西班牙购买行李箱和背包网站:Maletas Greenwich
2019/10/08 全球购物
Farfetch澳大利亚官网:Farfetch Australia
2020/04/26 全球购物
正规的求职信范文分享
2013/12/11 职场文书
外企财务年会演讲稿
2014/01/03 职场文书
社区敬老月活动实施方案
2014/02/17 职场文书
增员口号大全
2014/06/18 职场文书
2014年终工作总结范本
2014/12/15 职场文书
python 常用的异步框架汇总整理
2021/06/18 Python
基于JavaScript实现省市联动效果
2021/06/22 Javascript
Javascript之datagrid查询详解
2021/09/15 Javascript