2010-03-19 124 views
150

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

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

我也意识到这是一个不好的方式来实现阶乘,这只是一个例子。

+0

我没有PHP 5.3.0检查,但你尝试使用'全球$ factorial'? – kennytm 2010-03-19 20:02:29

+4

*(旁注)*一个Lamba是一个匿名函数,而上面是一个闭包。 – Gordon 2010-03-19 20:21:58

+1

Lambdas和闭包不是相互排斥的。事实上,有些人认为闭包必须是lambda,因为它是闭包(匿名函数)。例如Python你必须先给函数一个名字(取决于版本)。因为你必须给它一个名字,所以你不能内联,有人会说它不能成为一个封闭。 – 2010-05-06 13:15:02

回答

280

为了它的工作,你需要通过$阶乘作为参考

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

+1,另请参阅http://php100.wordpress .com/2009/04/13/php-y-combinator/ – user187291 2010-03-19 20:09:15

+0

这是奇怪的bc对象应该始终通过引用和anon传递。函数是对象... – ellabeauty 2012-08-14 21:31:46

+20

@ellabeauty在传递$ factorial的时候,它仍然是null(未定义),这就是为什么你必须通过引用传递它。 请注意,如果您在调用函数之前修改$ factorial,那么结果会随着引用传递而改变。 – 2012-09-13 21:48:21

22

我知道这可能不是一个简单的办法,但我了解一个函数式语言称为"fix"技术。来自Haskell的fix函数通常被称为Y combinator,它是最知名的fixed point combinators之一。

的固定点是,是由函数不变的值:一个函数的固定点f是任何X使得x   =   F(X)。固定点组合器是为任何函数f返回固定点的函数。由于y(f)是f的不动点,我们有y(f)  =  f(y(f))。

本质上,Y组合器创建一个新的函数,该函数接受原始的所有参数,再加上一个附加参数即递归函数。如何使用curried表示法更加明显。不要在括号中写入参数(f(x,y,...)),请将它们写在函数f x y ...之后。 Y组合器定义为Y f = f (Y f);或者使用递归函数的单个参数Y f x = f (Y f) x

由于PHP并不自动curry功能,这是一个破解,使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); 

注意这几乎是一样的单纯缝合解决方案其他人发布,但功能fix为您创建关闭。定点组合器比使用闭包要稍微复杂一些,但更为一般,还有其他用途。虽然闭包方法更适合于PHP(这不是一种非常功能的语言),但最初的问题更多的是练习而不是生产,所以Y组合器是一种可行的方法。

+8

值得注意的是,'call_user_func_array()'作为圣诞节是缓慢的。 – Xeoncross 2011-05-09 21:02:22

+10

@Xeoncross与设置陆地速度记录的PHP其余部分相反? :P – 2011-05-09 21:12:49

+1

请注意,您现在可以(5.6+)使用参数解包而不是'call_user_func_array'。 – 2017-04-17 15:43:11

2

虽然它不适合实际使用,但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) 
0

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

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

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

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