关于尾递归的使用详解

 更新时间:2013年05月02日 11:00:23   作者:  
本篇文章介绍了,关于尾递归的使用详解。需要的朋友参考下

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

尾递归的概念
尾递归(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语言对尾递归的优化。在使用尾递归对代码进行优化的时候,必须先了解这门语言对尾递归的支持。

相关文章

  • VSCode+PHPstudy配置PHP开发环境的步骤详解

    VSCode+PHPstudy配置PHP开发环境的步骤详解

    这篇文章主要介绍了VSCode+PHPstudy配置PHP开发环境,本文分步骤给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • 详谈PHP中public,private,protected,abstract等关键字的用法

    详谈PHP中public,private,protected,abstract等关键字的用法

    下面小编就为大家分享一篇详谈PHP中public,private,protected,abstract等关键字的用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-12-12
  • php微信公众平台开发之获取用户基本信息

    php微信公众平台开发之获取用户基本信息

    本文介绍如何获得微信公众平台关注用户的基本信息,包括昵称、头像、性别等基本信息。下面小编把最近整理有关php微信公众平台开发之获取用户基本信息的相关内容分享给大家,有需要的朋友可以参考下
    2015-08-08
  • 在Laravel 中实现是否关注的示例

    在Laravel 中实现是否关注的示例

    今天小编就为大家分享一篇在Laravel 中实现是否关注的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-10-10
  • Yii2超好用的日期和时间组件(值得收藏)

    Yii2超好用的日期和时间组件(值得收藏)

    日期和时间组件在平时的项目开发中经常用到。今天小编抽个时间谈谈yii2中比较好用的时间组件
    2016-05-05
  • php opendir()列出目录下所有文件的实例代码

    php opendir()列出目录下所有文件的实例代码

    这篇文章主要介绍了php opendir()列出目录下所有文件的实例代码的相关资料,需要的朋友可以参考下
    2016-10-10
  • Yii2如何批量添加数据

    Yii2如何批量添加数据

    批量添加这个操作,在实际开发中经常用得到,今天小编抽空给大家整理些有关yii2批量添加的问题,感兴趣的朋友一起看看吧
    2016-05-05
  • 百度地图API应用之获取用户的具体位置

    百度地图API应用之获取用户的具体位置

    以前一直用高德地图的API,后来因为需求,必须用百度的地图API。这些API都已经封装的很智能了,只需要一定的编程思维,就很简单可以实现功能。
    2014-06-06
  • php 可变函数使用小结

    php 可变函数使用小结

    PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。本文重点给大家介绍php 可变函数使用小结,感兴趣的朋友一起看看吧
    2018-06-06
  • php如何把表单内容提交到数据库

    php如何把表单内容提交到数据库

    这篇文章主要介绍了php如何吧表单内容提交到数据库,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07

最新评论