关于递归:匿名递归PHP函数

Anonymous recursive PHP functions

是否可以使用递归和匿名的PHP函数? 这是我试图让它工作,但它没有传递函数名称。

1
2
3
4
5
$factorial = function( $n ) use ( $factorial ) {
    if( $n <= 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );

我也知道这是实现阶乘的一种不好的方法,它只是一个例子。


为了使其工作,您需要传递$ factorial作为参考

1
2
3
4
5
$factorial = function( $n ) use ( &$factorial ) {
    if( $n == 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );


我知道这可能不是一个简单的方法,但我从函数式语言中学到了一种称为"修复"的技术。来自Haskell的fix函数更通常被称为Y组合器,它是最着名的定点组合器之一。

固定点是由函数保持不变的值:函数f的固定点是任何x,使得x&nbsp; =&nbsp; f(x)。定点组合器y是返回任何函数f的固定点的函数。由于y(f)是f的固定点,我们有y(f)&nbsp; =&nbsp; f(y(f))。

本质上,Y组合器创建一个新函数,它接受原始的所有参数,以及一个附加参数,即递归函数。使用curry表示法,这是如何工作的更明显。而不是在括号(f(x,y,...))中写入参数,而是在函数之后写出它们:f x y ...。 Y组合子定义为Y f = f (Y f);或者,使用递归函数的单个参数,Y f x = f (Y f) x

由于PHP不会自动调整函数,因此使fix工作有点麻烦,但我认为它很有趣。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function fix( $func )
{
    return function() use ( $func )
    {
        $args = func_get_args();
        array_unshift( $args, fix($func) );
        return call_user_func_array( $func, $args );
    };
}

$factorial = function( $func, $n ) {
    if ( $n == 1 ) return 1;
    return $func( $n - 1 ) * $n;
};
$factorial = fix( $factorial );

print $factorial( 5 );

请注意,这与其他人发布的简单闭包解决方案几乎相同,但函数fix会为您创建闭包。固定点组合器比使用闭合器稍微复杂一些,但更通用,并具有其他用途。虽然闭包方法更适合PHP(这不是一种非常功能的语言),但原始问题更多的是练习而不是生产,因此Y组合器是一种可行的方法。


虽然它不适合实际使用,但C级扩展mpyw-junks / phpext-callee提供匿名递归而不分配变量。

1
2
3
4
5
6
7
<?php

var_dump((function ($n) {
    return $n < 2 ? 1 : $n * callee()($n - 1);
})(5));

// 5! = 5 * 4 * 3 * 2 * 1 = int(120)

您可以在PHP 7.1+中使用Y Combinator,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function Y
($le)
{return
    (function ($f)
     {return
        $f($f);
     })(function ($f) use ($le)
        {return
            $le(function ($x) use ($f)
                {return
                    $f($f)($x);
                });
        });
}

$le =
function ($factorial)
{return
    function
    ($n) use ($factorial)
    {return
        $n < 2 ? $n
        : $n * $factorial($n - 1);
    };
};

$factorial = Y($le);

echo $factorial(1) . PHP_EOL; // 1
echo $factorial(2) . PHP_EOL; // 2
echo $factorial(5) . PHP_EOL; // 120

玩它:https://3v4l.org/7AUn2

源代码来自:https://github.com/whitephp/the-little-phper/blob/master/src/chapter_9.php


在较新版本的PHP中,您可以这样做:

1
2
3
4
5
6
7
8
9
10
$x = function($depth = 0) {
    if($depth++)
        return;

    $this($depth);
    echo"hi
"
;
};
$x = $x->bindTo($x);
$x();

这可能会导致奇怪的行为。