Récursion anonyme en C# propose une excellente discussion sur ce sujet.
La récursion est belle et les lambdas sont l'abstraction ultime. Mais comment les utiliser ensemble ? Les lambdas sont fonctions anonymes et la récursion nécessite des noms...
Puisque le sujet est revenu sur le tapis, voici un exemple d'utilisation du Y-combinator :
// This is the combinator
public static Func<A,R> Y<A,R>( Func<Func<A,R>, Func<A,R>> f )
{
Func<A,R> g = null;
g = f( a => g(a) );
return g;
}
Voici une utilisation pour appeler une fonction anonyme et récursive ...
Func<int,int> exp = Y<int,int>( e => x => ( x <=1 ) ? 1 : x * e( x - 1 ) );
Console.WriteLine( exp(5) );
Vous noterez que si vous n'utilisez pas le combinateur en Y et que vous configurez la récursion avec seulement le délégué, vous n'obtenez pas une récursion correcte. Par exemple ...
// This is BAD. Do not do this!
Func<int,int> badRec = null;
badRec = x => ( x <= 1 ) ? 1 : x * badRec( x - 1 );
Mais tout fonctionne bien...
Console.WriteLine( badRec(5) );
// Output
// 120
Mais essayez ceci...
Func<int,int> badRec = null;
badRec = x => ( x <= 1 ) ? 1 : x * badRec( x - 1 );
Func<int,int> badRecCopy = badRec;
badRec = x => x + 1;
Console.WriteLine( badRec(4) );
Console.WriteLine( badRecCopy(5) );
// Output
// 5
// 25
Quoi ? !?
Vous voyez, après la ligne badRec = x => x + 1;
le délégué que vous avez en réalité est celui-ci ...
badRecCopy = x => ( x <= 1 ) ? 1 : x * ( (x+1)-1 );
Donc, badRec incrémente la valeur de 1, ce que nous attendons. (4+1=5)
mais badRecCopy renvoie en fait le carré de la valeur (5*( (5+1)-1 )
ce à quoi nous ne nous attendions certainement pas.
Si vous utilisez le Y-combinator, cela fonctionnera comme prévu...
Func<int,int> goodRec = Y<int,int>( exp => x => ( x <=1 ) ? 1 : x * exp( x - 1 ) );
Func<int,int> goodRecCopy = goodRec;
Et vous obtenez ce que vous attendez.
goodRec = x => x + 1;
Console.WriteLine( goodRec(4) );
Console.WriteLine( goodRecCopy(5) );
// Output
// 5
// 120
Vous pouvez en savoir plus sur le Y-combinator (Lien PDF).