私がラムダ式を悪夢と表現したのを聞いた方もいるでしょう。
実際使ってみてどこが悪夢なんだと感じられた方も多いでしょう。
便利だからどんどん使っていこうと思っている方もいるでしょう。
でも、一般の人が理解できる範囲内で使ってください。
ちょっと階乗のプログラムを作ってみましょう。
こんな感じですね。
class Fact1
{
static int f(int x)
{
return x == 0 ? 1 : x * f(x - 1);
}
public void Factorial()
{
for (int i = 1; i < 10; i++)
Console.WriteLine(f(i));
}
}
それではラムダ式で各前段階として匿名デリゲートで書いてみましょう。
class Fact2
{
private Func<int, int> f = null;
public void Factorial()
{
f = delegate(int x)
{
return x == 0 ? 1 : x * this.f(x - 1);
};
for (int i = 1; i < 10; i++)
Console.WriteLine(f(i));
}
}
さて this.f はカッコ悪いですよね。
でもこれは自分を呼ばなきゃいけないので自己言及でどうにもなりません。
delegate(int x)
{
return x == 0 ? 1 : x * 【ここに名前が書けない】(x - 1);
};
そこで、この deligate を含む deligate を作ります。
Func<Func<int, int>,Func<int, int>> g =
delegate(Func<int, int> f)
{
return delegate(int x)
{
return x == 0 ? 1 : x * f(x - 1);
};
};
どうやって呼び出せばいいのでしょうか?
g(f)なんて形式では呼び出せません、困ってしまいます。
そんなときに救世主のように寄ってくるのが自己言及の悪魔、不動点演算子(Y Combinator)です。
delegate T Y<T>(Y<T> y); とした瞬間に自分を食って必要なクラスを取り出せてしまいます。
Y(Y)(g) で f が取り出せます。
class Fact3
{
delegate T Y<T>(Y<T> y);
public void Factorial()
{
Func<Func<int, int>,Func<int, int>> g =
delegate(Func<int, int> f)
{
return delegate(int x)
{
return x == 0 ? 1 : x * f(x - 1);
};
};
Y<Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>>> Y =
delegate(Y<Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>>> y)
{
return delegate(Func<Func<int, int>, Func<int, int>> z)
{
return delegate(int x)
{
return z(y(y)(z))(x);
};
};
};
for (int i = 1; i < 10; i++)
Console.WriteLine(Y(Y)(g)(i));
}
}
さて、ラムダ式で書いてみます。
でもお願いですから実験だけの世界にしておいてください。
class Fact4
{
delegate T Y<T>(Y<T> y);
public void Factorial()
{
Func<Func<int, int>, Func<int, int>> g = f => x => x == 0 ? 1 : x * f(x - 1);
Y<Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>>> Y = y => f => x => f(y(y)(f))(x);
for (int i = 1; i < 10; i++)
Console.WriteLine(Y(Y)(g)(i));
}
}
幸いなことにラムダ式の左辺に var は使えません。
var g = f => x => x == 0 ? 1 : x * f(x - 1);
var Y = y => f => x => f(y(y)(f))(x);
こんな暗号のような訳のわからない(私だけだと言われそうですが)コードは少なくても見なくて済みそうです。