PHP里的尾递归及其优化?
来源:互联网 发布:批量卸载软件 编辑:程序博客网 时间:2024/06/05 10:39
不同的语言对尾递归的支持都有所不同,编译器的优化也不尽相同。我们之前看了C语言的尾递归,那么在PHP里又是如何的呢?
PHP对尾递归没有优化效果
先来看下实验。
<?phpfunction factorial($n){if($n == 0) { return 1; } return factorial($n-1) * $n; } var_dump(factorial(100));?>
如果安装了XDebug的话,可能会遇到如下错误:
1
Fatal error: Maximum
function
nesting level of
'100'
reached, aborting!
这是XDebug的一个保护机制,可以通过max_nesting_level选项来设置。放开设置的话,程序还是能够正常运行的。
即便代码能正常运行,只要我们不断增大参数,程序迟早会报错:
1
Fatal error: Allowed memory size of … bytes exhausted
为什么呢?简单点说就是递归造成了栈溢出。按照之前的思路,我们可以试下用尾递归来消除递归对栈的影响,提高程序的效率。
<?phpfunction factorial($n, $acc){ if($n == 0) { return $acc; } return factorial($n-1, $acc * $n);}var_dump(factorial(100, 1));?>
XDebug同样报错,并且程序的执行时间并没有明显变化。
1
Fatal error: Maximum
function
nesting level of
'100'
reached, aborting!
事实证明,尾递归在php中是没有任何优化效果的。
PHP如何消除递归
先看看下面的源代码:
<?phpfunction factorial($n, $accumulator = 1) { if ($n == 0) { return $accumulator; } return function() use($n, $accumulator) { return factorial($n - 1, $accumulator * $n); };}function trampoline($callback, $params) { $result = call_user_func_array($callback, $params); while (is_callable($result)) { $result = $result(); } return $result;}var_dump(trampoline('factorial', array(100)));?>
现在XDebug不再警报效率问题了。
注意到trampoline()函数没?简单点说就是利用高阶函数消除递归。想更进一步了解 call_user_func_array,可以参看这篇文章:PHP函数补完:call_user_func()与call_user_func_array()
还有很多别的方法可以用来规避递归引起的栈溢出问题,比如说Python中可以通过装饰器和异常来消灭尾调用,让人有一种别有洞天的感觉。
小结
在C中的尾递归优化是gcc编译器做的。一般的线性递归修改成为尾递归最大的优势在于减少了递归调用栈的开销。从php那个例子就明显看出来递归开销对程序的影响。但是并不是所有语言都支持尾递归的,即使支持尾递归的语言也一般是在编译阶段对尾递归进行优化,比如C语言对尾递归的优化。在使用尾递归对代码进行优化的时候,必须先了解这门语言对尾递归的支持。
在PHP里,除非能提升代码可读性,否则没有必要使用递归;迫不得已之时,最好考虑使用Tail Call或Trampoline等技术来规避潜在的栈溢出问题。
- PHP里的尾递归及其优化?
- Maximum function nesting level of '100' reached, aborting!--漫谈递归:PHP里的尾递归及其优化
- 二分查找的非递归、递归实现及其优化
- 递推,递归及其优化
- 尾调用尾递归及其优化(笔记)
- php实现递归的三种基本方式及其说明
- js递归的优化(尾递归)
- PHP递归读取中文文件夹里的中文文件
- 要优化的sql语句 递归查询不包含本部门及其子部门
- 递归优化之尾递归
- 递归及尾递归优化
- 递归及尾递归优化
- 递归优化之尾递归
- 数据结构里的一些排序算法及其初级优化 v1.0
- 浅谈尾递归的优化方式
- lists优化,尾递归的重要一点
- 快速排序的尾递归优化
- 浅谈尾递归的优化方式
- 20170727 JAVA练习题:运用if else根据分数分成绩等级
- JavaScript的选择结构
- 知识点统计
- (多校)hdu 6050 Funny Function
- 阶乘因式分解
- PHP里的尾递归及其优化?
- 0727 Java的标识符
- selenium学习笔记_20170727
- 20170727 JAVA练习:会员信息录入
- 在linux下怎么解压和压缩tar.xz文件
- QQ第三方登陆(在Fragment中运行)
- 25句经典语录 带你成长
- Android应用启动时白色闪屏原因及解决办法
- 接口 Map<K,V>的用法