关于尾递归的使用详解


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基础知识:类与对象(4) 范围解析操作符(::)
Dec 13 PHP
fleaphp crud操作之find函数的使用方法
Apr 23 PHP
php学习之流程控制实现代码
Jun 09 PHP
php实现统计网站在线人数的方法
May 12 PHP
Zend Framework框架教程之Zend_Db_Table_Rowset用法实例分析
Mar 21 PHP
Linux(CentOS)下PHP扩展PDO编译安装的方法
Apr 07 PHP
php删除数组指定元素实现代码
May 03 PHP
thinkPHP3.2.3结合Laypage实现的分页功能示例
May 28 PHP
PHP设计模式之观察者模式定义与用法示例
Aug 04 PHP
PHP+jQuery实现即点即改功能示例
Feb 21 PHP
PHP生成zip压缩包的常用方法示例
Aug 22 PHP
PHP+Mysql分布式事务与解决方案深入理解
Feb 27 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
用Javascript 获取页面元素的位置的代码
2009/09/25 Javascript
javaScript 简单验证代码(用户名,密码,邮箱)
2009/09/28 Javascript
JS小框架 fly javascript framework
2009/11/26 Javascript
锋利的jQuery 第三章章节总结的例子
2010/03/23 Javascript
juqery 学习之三 选择器 可见性 元素属性
2010/11/25 Javascript
网站如何做到完全不需要jQuery也可以满足简单需求
2013/06/27 Javascript
js时间日期格式化封装函数
2014/12/02 Javascript
JavaScript基本类型值-Undefined、Null、Boolean
2017/02/23 Javascript
React Native中的RefreshContorl下拉刷新使用
2017/10/09 Javascript
vuex的使用及持久化state的方式详解
2018/01/23 Javascript
详解javascript中的变量提升和函数提升
2018/05/24 Javascript
微信小程序中的video视频实现 自定义播放按钮、封面图、视频封面上文案
2020/01/02 Javascript
基于jsbarcode 生成条形码并将生成的条码保存至本地+源码
2020/04/27 Javascript
原生js实现自定义滚动条
2021/01/20 Javascript
[58:12]Ti4第二日主赛事败者组 LGD vs iG 3
2014/07/21 DOTA
[01:03:41]完美世界DOTA2联赛PWL S3 DLG vs Phoenix 第一场 12.17
2020/12/19 DOTA
python之super的使用小结
2018/08/13 Python
keras获得某一层或者某层权重的输出实例
2020/01/24 Python
Python学习之os模块及用法
2020/06/03 Python
python中常见错误及解决方法
2020/06/21 Python
详解Python利用configparser对配置文件进行读写操作
2020/11/03 Python
解决pycharm导入numpy包的和使用时报错:RuntimeError: The current Numpy installation (‘D:\\python3.6\\lib\\site-packa的问题
2020/12/08 Python
python中用ggplot绘制画图实例讲解
2021/01/26 Python
如何在网站上添加谷歌定位信息
2016/04/16 HTML / CSS
Crabtree & Evelyn英国官网:瑰珀翠护手霜、香水、沐浴和身体护理
2018/04/26 全球购物
美国迪克体育用品商店:DICK’S Sporting Goods
2018/07/24 全球购物
澳大利亚领先的孕妇服装品牌:Mamaway
2018/08/14 全球购物
Shell编程面试题
2016/05/29 面试题
研究生自我鉴定范文
2013/10/30 职场文书
小松树教学反思
2014/02/11 职场文书
阳光体育活动方案
2014/02/16 职场文书
校园公益广告语
2014/03/13 职场文书
售后服务承诺书范文
2014/03/26 职场文书
新闻报道策划方案
2014/06/11 职场文书
2016年公司新年寄语
2015/08/17 职场文书
Python语言内置数据类型
2022/02/24 Python