Est-il possible d'avoir une fonction PHP à la fois récursive et anonyme? Ceci est ma tentative de le faire fonctionner, mais il ne passe pas dans le nom de la fonction.
$factorial = function( $n ) use ( $factorial ) {
if( $n <= 1 ) return 1;
return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );
Je suis également conscient que c'est une mauvaise façon de mettre en œuvre factorielle, ce n'est qu'un exemple.
Pour que cela fonctionne, vous devez utiliser $ factorial comme référence.
$factorial = function( $n ) use ( &$factorial ) {
if( $n == 1 ) return 1;
return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );
Je sais que cette approche n’est peut-être pas simple, mais j’ai découvert une technique appelée "réparer" à partir de langages fonctionnels. La fonction fix
de Haskell est connue plus généralement sous le nom de Y Combinator , qui est l’un des combinateurs les plus connus de points fixes .
Un point fixe est une valeur inchangée par une fonction: un point fixe d'une fonction f est quelconque x tel que x = f (x). Un combinateur à point fixe y est une fonction qui renvoie un point fixe pour toute fonction f. Puisque y(f) est un point fixe de f, nous avons y (f) = f (y (f)).
Essentiellement, le Y Combinator crée une nouvelle fonction qui prend tous les arguments de l’original, plus un argument supplémentaire qui est la fonction récursive. Comment cela fonctionne est plus évident en utilisant la notation au curry. Au lieu d'écrire les arguments entre parenthèses (f(x,y,...)
), écrivez-les après la fonction: f x y ...
. Le Y Combinator est défini comme Y f = f (Y f)
; ou, avec un seul argument pour la fonction récursive, Y f x = f (Y f) x
.
Puisque PHP ne fonctionne pas automatiquement curry , c'est un peu un bidouillage de faire fonctionner fix
, mais je pense que c'est intéressant.
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 );
Notez que ceci est presque identique aux solutions de fermeture simples publiées par d’autres, mais la fonction fix
crée la fermeture pour vous. Les combinateurs à points fixes sont légèrement plus complexes que l'utilisation d'une fermeture, mais sont plus généraux et ont d'autres utilisations. Bien que la méthode de fermeture convienne mieux à PHP (qui n'est pas un langage très fonctionnel), le problème initial étant davantage un exercice que pour la production, le Y Combinator est une approche viable.
Bien que ce ne soit pas pour un usage pratique, l’extension de niveau C mpyw-junks/phpext-callee fournit une récursivité anonyme sans affecter de variables.
<?php
var_dump((function ($n) {
return $n < 2 ? 1 : $n * callee()($n - 1);
})(5));
// 5! = 5 * 4 * 3 * 2 * 1 = int(120)
Dans les nouvelles versions de PHP, vous pouvez le faire:
$x = function($depth = 0) {
if($depth++)
return;
$this($depth);
echo "hi\n";
};
$x = $x->bindTo($x);
$x();
Cela peut potentiellement conduire à un comportement étrange.
Vous pouvez utiliser Y Combinator dans PHP 7.1+ comme ci-dessous:
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
Jouez avec: https://3v4l.org/7AUn2
Codes source de: https://github.com/whitephp/the-little-phper/blob/master/src/chapter_9.php