关于尾递归的使用详解


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中冒号、endif、endwhile、endfor使用介绍
Apr 28 PHP
php HtmlReplace输入过滤安全函数
Jul 03 PHP
php中Smarty模板初体验
Aug 08 PHP
php判断GIF图片是否为动画的方法
Sep 04 PHP
php中strtotime函数用法详解
Nov 15 PHP
php数组键名技巧小结
Feb 17 PHP
ecshop 2.72如何修改后台访问地址
Mar 03 PHP
PHP实现远程下载文件到本地
May 17 PHP
简单谈谈php浮点数精确运算
Mar 10 PHP
php封装的图片(缩略图)处理类完整实例
Oct 19 PHP
php实现的rc4加密解密类定义与用法示例
Aug 16 PHP
PHP FileSystem 文件系统常用api整理总结
Jul 12 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
用 PHP5 轻松解析 XML
2006/12/04 PHP
php设计模式 Composite (组合模式)
2011/06/26 PHP
PHP使用stream_context_create()模拟POST/GET请求的方法
2016/04/02 PHP
什么是JavaScript
2009/08/13 Javascript
javascript客户端解决方案 缓存提供程序
2010/07/14 Javascript
jquery中this的使用说明
2010/09/06 Javascript
使用javascript控制cookie显示和隐藏背景图
2014/02/12 Javascript
js实现动态加载脚本的方法实例汇总
2015/11/02 Javascript
SWFUpload多文件上传及文件个数限制的方法
2016/05/31 Javascript
jQuery UI结合Ajax创建可定制的Web界面
2016/06/22 Javascript
JS如何设置iOS中微信浏览器的title
2016/11/22 Javascript
修改 bootstrap table 默认detailRow样式的实例代码
2017/07/21 Javascript
微信小程序scroll-view点击项自动居中效果的实现
2020/03/25 Javascript
python学习手册中的python多态示例代码
2014/01/21 Python
python进阶_浅谈面向对象进阶
2017/08/17 Python
浅谈Python处理PDF的方法
2017/11/10 Python
python kmeans聚类简单介绍和实现代码
2018/02/23 Python
python pygame模块编写飞机大战
2018/11/20 Python
Django保护敏感信息的方法示例
2019/05/09 Python
Pandas中DataFrame交换列顺序的方法实现
2020/12/14 Python
纯css3实现图片翻牌特效
2015/03/10 HTML / CSS
瑞士灯具购物网站:Lampenwelt.ch
2018/07/08 全球购物
舞会礼服和舞会鞋:PromGirl
2019/04/22 全球购物
龟牌英国商店:Turtle Wax Brand Store UK
2019/07/02 全球购物
财务助理岗位职责
2013/11/10 职场文书
八项规定整改措施
2014/02/12 职场文书
劲霸男装广告词
2014/03/21 职场文书
《卖木雕的少年》教学反思
2014/04/11 职场文书
活动总结模板
2014/05/09 职场文书
事业单位考核材料
2014/05/21 职场文书
车贷收入证明范本
2014/09/14 职场文书
2015年秋季新学期寄语
2015/03/25 职场文书
大学生求职意向书
2015/05/11 职场文书
敬业奉献模范事迹材料(2016精选版)
2016/02/26 职场文书
【超详细】八大排序算法的各项比较以及各自特点
2021/03/31 Python
vue实现Toast组件轻提示
2022/04/10 Vue.js