匿名递归 PHP 函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2480179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 06:38:35  来源:igfitidea点击:

Anonymous recursive PHP functions

phprecursionlambdaclosuresanonymous-function

提问by Kendall Hopkins

Is it possible to have a PHP function that is both recursive and anonymous? This is my attempt to get it to work, but it doesn't pass in the function name.

是否有可能同时具有递归和匿名的 PHP 函数?这是我试图让它工作的尝试,但它没有传入函数名。

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

I'm also aware that this is a bad way to implement factorial, it's just an example.

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

回答by Derek H

In order for it to work, you need to pass $factorial as a reference

为了让它工作,你需要传递 $factorial 作为参考

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

回答by Kendall Hopkins

I know this might not be a simple approach, but I learned about a technique called "fix"from functional languages. The fixfunction from Haskell is known more generally as the Y combinator, which is one of the most well-known fixed point combinators.

我知道这可能不是一个简单的方法,但我从函数式语言中学到了一种称为“修复”的技术。fixHaskell的函数更普遍地称为Y 组合器,它是最著名的不动点组合器之一

A fixed point is a value that is unchanged by a function: a fixed point of a function fis any xsuch that x = f(x). A fixed point combinator yis a function that returns a fixed point for any function f. Since y(f) is a fixed point of f, we have y(f) = f(y(f)).

不动点是函数不变的值:函数f的不动点是任何满足x = f(x) 的 x。定点组合子y是一个函数,它为任何函数 f 返回一个不动点。由于 y(f) 是 f 的不动点,我们有 y(f) = f(y(f))。

Essentially, the Y combinator creates a new function that takes all the arguments of the original, plus an additional argument that's the recursive function. How this works is more obvious using curried notation. Instead of writing arguments in parentheses (f(x,y,...)), write them after the function: f x y .... The Y combinator is defined as Y f = f (Y f); or, with a single argument for the recursed function, Y f x = f (Y f) x.

本质上,Y 组合器创建了一个新函数,该函数采用原始函数的所有参数,以及一个作为递归函数的附加参数。使用柯里化符号可以更明显地了解其工作原理。不要将参数写在括号 ( f(x,y,...)) 中,而是将它们写在函数之后:f x y ...。Y 组合子定义为Y f = f (Y f);或者,递归函数只有一个参数,Y f x = f (Y f) x.

Since PHP doesn't automatically curryfunctions, it's a bit of a hack to make fixwork, but I think it's interesting.

由于 PHP 不会自动柯里化函数,因此使其fix工作有点麻烦,但我认为这很有趣。

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 );

Note this is almost the same as the simple closure solutions others have posted, but the function fixcreates the closure for you. Fixed point combinators are slightly more complex than using a closure, but are more general, and have other uses. While the closure method is more suitable for PHP (which isn't a terribly functional language), the original problem is more of an exercise than for production, so the Y combinator is a viable approach.

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

回答by mpyw

Although it is not for practial usage, The C-level extension mpyw-junks/phpext-calleeprovides anonymous recursion without assigning variables.

虽然不用于实际使用,但 C 级扩展mpyw-junks/phpext-callee提供了匿名递归,无需分配变量

<?php

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

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

回答by jgmjgm

In newer versions of PHP you can do this:

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

$x = function($depth = 0) {
    if($depth++)
        return;

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

This can potentially lead to strange behaviour.

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

回答by Lei Fan

You can use Y Combinator in PHP 7.1+ as below:

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

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

Play with it: https://3v4l.org/7AUn2

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

Source codes from: https://github.com/whitephp/the-little-phper/blob/master/src/chapter_9.php

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

回答by Ayell

With an anonymous class (PHP 7+), without defining a variable:

使用匿名类(PHP 7+),无需定义变量:

echo (new class {
    function __invoke($n) {
        return $n < 2 ? 1 : $n * $this($n - 1);
    }
})(5);