关于尾递归的使用详解


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+MYSQL的文章管理系统(一)
Oct 09 PHP
一个php作的文本留言本的例子(一)
Oct 09 PHP
php获取网页内容方法总结
Dec 04 PHP
php strstr查找字符串中是否包含某些字符的查找函数
Jun 03 PHP
php常用Output和ptions/Info函数集介绍
Jun 19 PHP
php实例分享之二维数组排序
May 15 PHP
PHP的拦截器实例分析
Nov 03 PHP
调试WordPress中定时任务的相关PHP脚本示例
Dec 10 PHP
php similar_text()函数的定义和用法
May 12 PHP
详解php中的implements 使用
Jun 13 PHP
PHP实现redis限制单ip、单用户的访问次数功能示例
Jun 16 PHP
php实现的数组转xml案例分析
Sep 28 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
自定义PHP分页函数
2006/10/09 PHP
利用phpExcel实现Excel数据的导入导出(全步骤详细解析)
2013/11/26 PHP
php jquery 多文件上传简单实例
2013/12/23 PHP
ThinkPHP 3.2 数据分页代码分享
2014/10/14 PHP
php表单习惯用的正则表达式
2017/10/11 PHP
PHP实现基于PDO扩展连接PostgreSQL对象关系数据库示例
2018/03/31 PHP
js常用代码段收集
2011/10/28 Javascript
Visual Studio中js调试的方法图解
2014/06/30 Javascript
vue2.0+webpack环境的构造过程
2016/11/08 Javascript
BootStrap 模态框实现刷新网页并关闭功能
2017/01/04 Javascript
JavaScript 字符串数字左补位,右补位,取固定长度,截位扩展函数代码
2017/03/25 Javascript
微信小程序 本地数据读取实例
2017/04/27 Javascript
使用JS模拟锚点跳转的实例
2018/02/01 Javascript
利用Bootstrap Multiselect实现下拉框多选功能
2019/04/08 Javascript
JSX在render函数中的应用详解
2019/09/04 Javascript
JS错误处理与调试操作实例分析
2020/04/13 Javascript
vue使用echarts实现水平柱形图实例
2020/09/09 Javascript
python实现的一只从百度开始不断搜索的小爬虫
2013/08/13 Python
基于腾讯云服务器部署微信小程序后台服务(Python+Django)
2019/05/08 Python
django框架事务处理小结【ORM 事务及raw sql,customize sql 事务处理】
2019/06/27 Python
利用python画出AUC曲线的实例
2020/02/28 Python
Pandas把dataframe或series转换成list的方法
2020/06/14 Python
Django限制API访问频率常用方法解析
2020/10/12 Python
python实现视频压缩功能
2020/12/18 Python
意大利奢侈品网站:Italist
2016/08/23 全球购物
Clarks鞋美国官网:全球领军鞋履品牌
2017/05/13 全球购物
匡威意大利官方商店 :Converse意大利
2018/11/27 全球购物
写出程序把一个链表中的接点顺序倒排
2014/04/28 面试题
校园餐饮创业计划书
2014/01/10 职场文书
情侣吵架检讨书
2014/02/05 职场文书
《晏子使楚》教学反思
2014/02/08 职场文书
测量员岗位职责
2015/02/14 职场文书
病房管理制度范本
2015/08/06 职场文书
关于感恩的歌曲整理(8首)
2019/08/14 职场文书
HTML基础详解(下)
2021/10/16 HTML / CSS
nginx.conf配置文件结构小结
2022/04/08 Servers