ラムダ式が便利すぎて生きるのが辛い
2010.06.11 金 13:05:23
VS2008に搭載されたC# 3.0の大きな機能の一つ、ラムダ式。
従来での匿名デリゲートによる記述から、冗長な部分をごっそり取り除くことでより簡潔な記述ができるようになります。
これにより、簡単なことを簡単に書くという当たり前だけどなぜか他ではできないことができるようになります。
ラムダ式にもいろいろあるのですが、今回はProject Eularの問題61をもとに部分プログラムを示してみます。
私の場合はこれでもうおなかいっぱいです。
ということで、Project Eularの問題61。
http://odz.sakura.ne.jp/projecteuler/index.php?Problem%2061
とりあえずn角数(3≦n≦8)を適当な大きさ(maxとする)まで全て生成してしまわなければ始まらないでしょう。
ここではこのテーブル生成ルーチンを考えます。
判定を高速化するために、要素数maxのbool配列を用意し、フラグを立てていく戦略でいくことにしましょう。
つまり、bool[][] flagと用意し、たとえばflag[0][100]は100が三角数かどうか、flag[3][32]は32が六角数かどうか…flag[n][k]は値kが(n+3)角数であるかどうかを表す、というものです。
ここでラムダ式を使うとスマートに書けてしまいます。
bool[][] flag = new bool[6][];
Func<int, int>[] ts =
{
n => n * (n + 1) / 2,
n => n * n,
n => n * (3 * n - 1) / 2,
n => n * (2 * n - 1),
n => n * (5 * n - 3) / 2,
n => n * (3 * n - 2)
};
for(int i = 0; i < 6; i++)
{
bool[] tmp = (flag[i] = new bool[max]);
Func<int, int> f = ts[i];
int k = 0, n = 0;
while((k = f(n++)) < max)
tmp[k] = true;
}
Func<T1, T2>は、T1型の引数を受け取ってT2型の値を返すメソッドを示すデリゲートで、System名前空間に含まれています。 引数の数に応じてFunc<T1, T2, T3>などいくつか用意されています(ちなみに値を返さない場合にはAction<T>で)。
ここでn角数を求めるメソッドを一つ一つクラスメンバで書いてしまうのは論外でしょう。6回のループで回せなくなってしまいます。
ループで回せるようにデリゲート配列に入れてしまうのもぎこちなくて気持ち悪いですね。
匿名メソッドでこれを書こうとするならだいぶマシでしょう。
delegate(int n) {return n * (n + 1) / 2;}
三角数生成の関数はこんな風になります。な、長いぞ…
ここでラムダ式ではどうなるか。
n => n * (n + 1) / 2
式しか書いてありません。
nがパラメータ、右辺が値となるわけです。
型情報すら必要ないのは、コンパイラにより型推論されるからです。
これは書きやすいし読みやすい。
これが、ラムダ式の最も簡潔に書ける形です。
ただラムダ式でも、引数が複数ある場合は左辺に括弧が必要となります。また右辺が複数の文から成る場合はブラケット{...}が必要となります。
Func<int, int, int> sum = (a, b) => a + b;
Func<int, int, int> div = (a, b) => {a++; b++; return a / b;};
上はともかく、下はもはや簡潔とは言い難いでしょう。
(このプログラム自体には意味はないので一文で書けるじゃんってツッコミはナシで)
というわけで、ネタに困ったので放出したどうでもいい殴り書き記事でした!