IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

PHP与递归Recursion

火丁笔记 2012-07-30 23:58:01 累计浏览 9,248 次
本机暂存

    在程序设计中,递归(Recursion)是一个很常见的概念,合理使用递归,可以提升代码的可读性,但同时也可能会带来一些问题。

    下面以阶乘(Factorial)为例来说明一下递归的用法,实现代码为PHP:

    如果安装了XDebug的话,可能会遇到如下错误:

    Fatal error: Maximum function nesting level of ’100′ reached, aborting!

    注:这是XDebug的一个保护机制,可以通过max_nesting_level选项来设置。

    即便代码能正常运行,只要我们不断增大参数,程序迟早会报错:

    Fatal error:  Allowed memory size of … bytes exhausted

    为什么呢?简单点说就是递归造成了栈溢出。有几个方法可以用来规避这个问题,比如说利用尾调用(Tail Call)来消除递归对栈的影响。

    下面以Lua作为描述语言来说明,代码如下:

function factorial(n)
    if (n == 0) then
        return 1
    end

    return factorial(n - 1) * n
end

print(factorial(100))

    这段代码同样会遇到栈溢出的问题。那么到底什么是尾调用呢?如果一个函数在调用另外一个函数以后不再做别的事就称为尾调用。尾调用不会返回原来的函数,所以不需要额外的栈保留调用函数的数据。

function factorial(n, accumulator)
    accumulator = accumulator or 1

    if (n == 0) then
        return accumulator
    end

    return factorial(n - 1, accumulator * n)
end

print(factorial(100))

    关于Lua中尾调用的介绍可以参考:Proper Tail Recursion

    照猫画虎,我们用PHP来实现一个尾调用版本的阶乘:

    可惜一运行才发现PHP根本不支持尾调用!不过天无绝人之路,仔细研读维基百科中关于尾调用的介绍,你会发现里面提到了Trampoline的概念。简单点说就是利用高阶函数消除递归,依照这样的理论基础,我们可以把上面的尾调用代码改写成如下方式:

    看上去不错,不过我不得不向大家道个歉,用递归实现阶乘其实是个玩笑,实际上,无需使用递归,只要用一个循环就行了,印象中《代码大全》里专门提到了这一点:

    还有很多别的方法可以用来规避递归引起的栈溢出问题,比如说Python中可以通过装饰器来消灭尾调用,让人有一种别有洞天的感觉:

  • Tail Call Optimization Decorator (Python recipe)
  •     另外,Python之父关于为何不在Python中支持尾调用的博文也很有看头:

  • Tail Recursion Elimination
  • Final Words on Tail Calls
  •     除非能提升代码可读性,否则没有必要使用递归;迫不得已之时,最好考虑使用Tail Call或Trampoline等技术来规避潜在的栈溢出问题。

    同分类推荐文章

    1. 对基本有序的序列排序算法 (2026-06-11 17:46:49)
    2. Four Levels Of Customer Understanding (2026-05-22 21:00:00)
    3. 除法的意义 (2026-04-12 20:52:17)

    查看更多 算法 文章 →

    建议继续学习

    1. 使用gettext来支持PHP的多语言 (累计阅读 39,267)
    2. WordPress插件开发 -- 在插件使用数据库存储数据 (累计阅读 29,163)
    3. Paypal接口详细代码(PHP版,非API接口) (累计阅读 19,407)
    4. 为什么你写不好一个快速排序? 谈程序员的职业发展 (累计阅读 14,464)
    5. 我的PHP,Python和Ruby之路 (累计阅读 13,146)
    6. include(“./file.php”)和include(“file.php”)区别 (累计阅读 12,788)
    7. 15个最好的免费开源电子商务平台 (累计阅读 12,540)
    8. Redis消息队列的若干实现方式 (累计阅读 12,086)
    9. 到底什么是MVC? (累计阅读 11,865)
    10. 关于memcache分布式一致性hash (累计阅读 11,818)