2010-03-19 29 views
150

Ist es möglich, eine PHP-Funktion zu haben, die sowohl rekursiv als auch anonym ist? Dies ist mein Versuch, es zum Laufen zu bringen, aber es gibt den Funktionsnamen nicht weiter.Anonyme rekursive PHP-Funktionen

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

Ich bin mir auch bewusst, dass dies eine schlechte Möglichkeit ist, faktoriell zu implementieren, es ist nur ein Beispiel.

+0

Ich habe kein PHP 5.3.0 zu überprüfen, aber haben Sie versucht, 'global $ factorial' zu verwenden? – kennytm

+4

* (sidenote) * Ein Lamba ist eine anonyme Funktion, während das obige eine Closure ist. – Gordon

+1

Lambdas und Closures schließen sich nicht gegenseitig aus. In der Tat glauben einige Leute, dass eine Schließung Lambda sein muss, damit es eine Schließung ist (anonyme Funktion). Zum Beispiel Python muss man der Funktion zuerst einen Namen geben (abhängig von der Version).Weil Sie ihm einen Namen geben müssen, können Sie nicht inline und einige würden sagen, dass es eine Schließung zu disqualifizieren. –

Antwort

280

Damit es funktioniert, müssen Sie $ als

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

+1, siehe auch http: //php100.wordpress .com/2009/04/13/php-y-kombinator/ – user187291

+0

das ist seltsam bc objekte sollten immer durch referenz und anon übergeben werden. Funktionen sind Objekte ... – ellabeauty

+20

@ellabeauty in der Zeit $ factorial ist übergeben, es ist immer noch Null (nicht definiert), deshalb müssen Sie es als Referenz übergeben. Beachten Sie, dass sich das Ergebnis ändert, wenn es vor dem Aufruf der Funktion geändert wird, wenn es als Referenz übergeben wird. –

22

ich das vielleicht nicht ein einfacher Ansatz wissen Referenz factorial passieren, aber ich über eine Technik gelernt "fix" von funktionalen Sprachen genannt. Die fix Funktion von Haskell ist allgemeiner bekannt als die Y combinator, die eine der bekanntesten fixed point combinators ist.

ein Fixpunkt ist ein Wert, der durch eine Funktion unverändert ist: ein fester Punkt einer Funktion f ist jedes x, so daß x =     f (x). Ein Festkomma-Kombinator y ist eine Funktion, die einen festen Punkt für jede Funktion f zurückgibt. Da y (f) ein fester Punkt von f ist, haben wir y (f)   =   f (y (f)).

Im Wesentlichen erstellt der Y-Kombinator eine neue Funktion, die alle Argumente des Originals übernimmt, plus ein zusätzliches Argument, das die rekursive Funktion ist. Wie das funktioniert, ist in der Curry-Notation offensichtlich. Anstatt Argumente in Klammern zu schreiben (f(x,y,...)), schreiben Sie sie nach der Funktion: f x y .... Der Y-Kombinator ist definiert als Y f = f (Y f); oder, mit einem einzigen Argument für die rekursive Funktion, Y f x = f (Y f) x.

Da PHP nicht automatisch curry funktioniert, ist es ein bisschen ein Hack zu machen fix Arbeit, aber ich denke, es ist interessant.

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

Hinweis: Dies ist fast das gleiche wie die einfachen Verschlusslösungen andere Beiträge geschrieben haben, aber die Funktion fix schafft die Schließung für Sie. Festkomma-Kombinatoren sind etwas komplexer als die Verwendung eines Verschlusses, sind jedoch allgemeiner und haben andere Verwendungen. Während die Closure-Methode eher für PHP geeignet ist (was keine ausgesprochen funktionale Sprache ist), ist das ursprüngliche Problem eher eine Übung als für die Produktion, daher ist der Y-Kombinator ein praktikabler Ansatz.

+8

Es ist bemerkenswert, dass 'call_user_func_array()' als Weihnachten langsam ist. – Xeoncross

+10

@Xeoncross Im Gegensatz zum Rest von PHP, der den Landgeschwindigkeitsrekord einstellt? : P –

+1

Hinweis, Sie können nun (5.6+) das Argument Entpacken anstelle von 'call_user_func_array' verwenden. –

2

Obwohl es nicht für die praktische Verwendung ist, bietet die C-Level-Erweiterung mpyw-junks/phpext-callee anonyme Rekursion ohne Zuweisen von Variablen.

<?php 

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

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

In neueren Versionen von PHP können Sie dies tun:

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

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

Dies kann möglicherweise zu seltsamen Verhalten führen.