How to write a recursive lambda
Easy, isn’t it?
At first glance, this problem looks quite easy, isn’t it? Let’s try:
Func<int, int> factorial = x => x == 0 ? 1 : x * factorial(x - 1);
Unfortunately, the above code will fail to compile, because factorial
is used when unassigned.
So, how about we assign it before doing this:
Func<int, int> factorial = null;
factorial = x => x == 0 ? 1 : x * factorial(x - 1);
Yay! This time the code compiles! Problem solved, isn’t it?
Well, sort of, unless we continue to write:
var f2 = factorial;
factorial = i => i;
Console.WriteLine(f2(5));
This will output 20
instead of 120
, because f2
becomes the following when factorial = i => i
x => x == 0 ? 1 : x * (i => i)(x - 1)
So, what now?
Map from a function to another function
Consider the following map
function:
Func<Func<int, int>, Func<int, int>> map = f => x => x == 0 ? 1 : x * f(x - 1);
It takes a function f
, and returns a function that uses f
as if it’s a factorial function.
If f
is indeed a factorial function, then the resulting function will be a factorial function.
Fixed-points of a function
Generally, if x = g(x)
, then x
is a fixed-point of g
. Let’s say x = Fix(g)
.
In our case, because applying a factorial function f
to map
gives us a factorial function, a factorial function is actually a fixed-point of map
, which is Fix(map)
. So f = Fix(map)
.
Now let’s find Fix(g)
:
static Func<T, T> Fix<T>(Func<Func<T, T>, Func<T, T>> g)
{
// ...
}
By definition, we have x = g(x)
, because x = Fix(g)
, we have Fix(g) = g(Fix(g))
. So we have:
static Func<T, T> Fix<T>(Func<Func<T, T>, Func<T, T>> g)
{
return g(Fix(g));
}
Not quite yet, because we are here to work out a fixed-point definition of g
, not executing it, we need one more step:
static Func<T, T> Fix<T>(Func<Func<T, T>, Func<T, T>> g)
{
return x => g(Fix(g))(x);
}
Essentially this function is creating a function that gets the fixed-point function of g
and then calls g
.
Result
Now if we apply Fix(map)
to map
, which is map(Fix(map))
, it will return our factorial function. But Fix(map)
itself is aleady a factorial function!
Full source:
class Program
{
static void Main( string[] args )
{
Func<Func<int, int>, Func<int, int>> map
= fac => x => x == 0 ? 1 : x * fac(x - 1);
var factorial = Fix(map);
Console.WriteLine(factorial(5));
}
static Func<T, T> Fix<T>(Func<Func<T, T>, Func<T, T>> g)
{
return x => map(Fix(g))(x);
}
}