functional-php/trampoline

PHP 的 Trampoline 实现。

1.1.0 2016-10-25 20:12 UTC

This package is auto-updated.

Last update: 2024-09-21 20:41:39 UTC


README

Build Status Scrutinizer Code Quality Code Coverage Average time to resolve an issue Percentage of issues still open Chat on Gitter

Trampolines 是一种技术,用于在递归调用时避免调用栈溢出。这是因为 PHP 不执行尾调用优化。

有关尾调用优化(或 TCO)的更多信息,您可以阅读: http://stackoverflow.com/questions/310974/what-is-tail-call-optimization#answer-310980

有关 Trampolines 以及递归的整体更深入的定义,我推荐您阅读 http://blog.moertel.com/posts/2013-06-12-recursion-to-iteration-4-trampolines.html,该文使用 Python,但应该很容易理解。

安装

composer require functional-php/trampoline

基本用法

如果我们有以下递归函数

<?php

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

echo factorial(5);
// 120

我们只需要简单地将递归调用替换为对 bounce 函数的调用

<?php

use FunctionalPHP\Trampoline as T;

function factorial($n, $acc = 1) {
    return $n <= 1 ? $acc : T\bounce('factorial', $n - 1, $n * $acc);
};

echo T\trampoline('factorial', 5);
// 120

bouncetrampoline 函数接受 PHP 认为是有效的 callable 任何内容。

trampoline 函数还将接受由 bounce 创建的 Trampoline 实例,但在这种情况下将忽略任何参数。

辅助函数

如果您喜欢这种风格,也可以静态调用全局命名空间中的任何函数

<?php

use FunctionalPHP\Trampoline\Trampoline;

echo Trampoline::factorial(5);
// 120

echo Trampoline::strtoupper('Hello!');
// HELLO!

但是,这不会对命名空间内的函数起作用。

如果您想使用 trampoline 有一个现成的可调用函数,可以使用 trampoline_wrapper 辅助函数。它将创建一个包装函数,为您调用 trampoline 并返回结果。

<?php

use FunctionalPHP\Trampoline as T;

function factorial($n, $acc = 1) {
    return $n <= 1 ? $acc : T\bounce('factorial', $n - 1, $n * $acc);
};

$fact = T\trampoline_wrapper('factorial');

echo $fact(5);
// 120

替代方法

该库还包含一个替代实现,基于参数队列而不是 trampoline 运行尾递归函数,从而避免栈溢出风险。

您需要将 $this 变量用作递归函数。以下是使用第二种方法的阶乘示例。

<?php

use FunctionalPHP\Trampoline as T;

$fact = T\pool(function($n, $acc = 1) {
    return $n <= 1 ? $acc : $this($n - 1, $n * $acc);
});

echo $fact(5);
// 120

目前,仅支持匿名函数(即 Closure 类的实例)。但一旦 PHP 7.1 发布,您将能够使用任何可调用项。

从性能的角度来看,两种方法之间没有可测量的差异。

测试

您可以使用以下命令运行库的测试套件

composer test

测试报告将在 reports 目录中可用。

贡献

除了潜在的错误之外,不应该有太多要贡献的内容,但任何形式的贡献都受到欢迎!不要犹豫,提出问题或提交拉取请求。