聊城万拓网络科技-专业聊城网站建设、聊城网站制作、聊城网站优化、聊城做网站的品牌网站建设专家!

    您当前的位置是:首页 - 新闻动态 - 网站建设 » 浅谈一下PHP里的尾递归及其优化策略

    浅谈一下PHP里的尾递归及其优化策略
     发布时间:2014-04-27  点击次数: 次   作者:万拓网络  来源:lcbaituo.com  Tags:
    不同的语言对尾递归的支持都有所不同,编译器的优化也不尽相同。我们之前看了C语言的尾递归,那么在PHP里又是如何的呢?
    PHP对尾递归没有优化效果

    先来看下实验。

    <?php
    function factorial($n)
    {
        if($n == 0) {
            return 1;
        }   
        return factorial($n-1) * $n;
    }
     
    var_dump(factorial(100));
    ?>

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

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

    这是XDebug的一个保护机制,可以通过max_nesting_level选项来设置。放开设置的话,程序还是能够正常运行的。

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

    Fatal error:  Allowed memory size of … bytes exhausted

    为什么呢?简单点说就是递归造成了栈溢出。按照之前的思路,我们可以试下用尾递归来消除递归对栈的影响,提高程序的效率。

    <?php
    function factorial($n, $acc)
    {
        if($n == 0) {
            return $acc;
        }
        return factorial($n-1, $acc * $n);
    }


    var_dump(factorial(100, 1));
    ?>

    XDebug同样报错,并且程序的执行时间并没有明显变化。

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

    事实证明,尾递归在php中是没有任何优化效果的。
    PHP如何消除递归

    先看看下面的源代码:

    <?php
    function 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()函数没?简单点说就是利用高阶函数消除递归。

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

    在C中的尾递归优化是gcc编译器做的。一般的线性递归修改成为尾递归最大的优势在于减少了递归调用栈的开销。从php那个例子就明显看出来递归开销对程序的影响。但是并不是所有语言都支持尾递归的,即使支持尾递归的语言也一般是在编译阶段对尾递归进行优化,比如C语言对尾递归的优化。在使用尾递归对代码进行优化的时候,必须先了解这门语言对尾递归的支持。

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


    分享到:
    上一篇:一段经典IE6下PNG背景透明方法的JS代码,经测试OK,代码放在<head></head>之间
    下一篇:动态网站软件开发所需的Web构件
     

    本站业务:聊城网站建设-聊城网站制作-聊城做网站