@saku_karamomo 素LINQだとあまりこういうケースないですけどねぇ URL ここまで行くとああクエリ構文の方が手段として正当だなって思えます
2012-07-26 19:00:50 via Krile2 to @saku_karamomo
というつぶやきをみて、確かになぁと思ったけど、それでもあえて「メソッド構文で書きたい」という信念を貫きたい*1という人向け。
要は、値の組を後ろに伝搬させればいいということなのでTupleを使えばいい…!ということでSelectManyが一発ならこう書けます。
var values = Enumerable.Range(1, 10) .SelectMany(i => Enumerable.Range(0, i * i), Tuple.Create); foreach (var i in values) { Console.WriteLine("{0} {1}", i.Item1, i.Item2); }
おっいい感じじゃね?ということでSelectManyを重ねるとちょっと残念になっちゃいます。
var values = Enumerable.Range(1, 10) .SelectMany(i => Enumerable.Range(0, i * i), Tuple.Create) .SelectMany(t => Enumerable.Range(0, t.Item2 * t.Item2), Tuple.Create); foreach (var i in values) { // あれ・・・値を辿るのがめんどくさい… Console.WriteLine("{0} {1} {2}", i.Item1.Item1, i.Item1.Item2, i.Item2); }
ふむ、i.Item1.Item1とか嫌になりますよね。SelectManyを更に重ねていくとi.Item1.Item1.Item1.Item1....とか発狂しそうです。さっきのConsole.WriteLineの行は、こう書きたいです。
Console.WriteLine("{0} {1} {2}", i.Item1, i.Item2, i.Item3);
なら、Tuple
public static class TupleUtil { public static Tuple<T1, T2, T3> Append<T1, T2, T3>(Tuple<T1, T2> t, T3 v) { return Tuple.Create(t.Item1, t.Item2, v); } }
こんな風に書けます。
var values = Enumerable.Range(1, 10) .SelectMany(i => Enumerable.Range(0, i * i), Tuple.Create) .SelectMany(t => Enumerable.Range(0, t.Item2 * t.Item2), TupleUtil.Append); foreach (var i in values) { Console.WriteLine("{0} {1} {2}", i.Item1, i.Item2, i.Item3); }
うん、いい感じ。ということで、BCLに定義されてるTupleの引数の最大値は8個なので8個まで淡々とオーバーロードを増やした結果こんなクラスができます。
public static class TupleUtil { public static Tuple<T1, T2, T3> Append<T1, T2, T3>(Tuple<T1, T2> t, T3 v) { return Tuple.Create(t.Item1, t.Item2, v); } public static Tuple<T1, T2, T3, T4> Append<T1, T2, T3, T4>(Tuple<T1, T2, T3> t, T4 v) { return Tuple.Create(t.Item1, t.Item2, t.Item3, v); } public static Tuple<T1, T2, T3, T4, T5> Append<T1, T2, T3, T4, T5>(Tuple<T1, T2, T3, T4> t, T5 v) { return Tuple.Create(t.Item1, t.Item2, t.Item3, t.Item4, v); } public static Tuple<T1, T2, T3, T4, T5, T6> Append<T1, T2, T3, T4, T5, T6>(Tuple<T1, T2, T3, T4, T5> t, T6 v) { return Tuple.Create(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, v); } public static Tuple<T1, T2, T3, T4, T5, T6, T7> Append<T1, T2, T3, T4, T5, T6, T7>(Tuple<T1, T2, T3, T4, T5, T6> t, T7 v) { return Tuple.Create(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6, v); } public static Tuple<T1, T2, T3, T4, T5, T6, T7, T8> Append<T1, T2, T3, T4, T5, T6, T7, T8>(Tuple<T1, T2, T3, T4, T5, T6, T7> t, T8 v) { return new Tuple<T1, T2, T3, T4, T5, T6, T7, T8>(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6, t.Item7, v); } }
こうしておくと、ここまでSelectManyを連打できます。
var values = Enumerable.Range(1, 10) .SelectMany(i => Enumerable.Range(0, i * i), Tuple.Create) .SelectMany(t => Enumerable.Range(0, t.Item2 * t.Item2), TupleUtil.Append) .SelectMany(t => Enumerable.Range(0, t.Item3 * t.Item3), TupleUtil.Append) .SelectMany(t => Enumerable.Range(0, t.Item4 * t.Item4), TupleUtil.Append) .SelectMany(t => Enumerable.Range(0, t.Item5 * t.Item5), TupleUtil.Append) .SelectMany(t => Enumerable.Range(0, t.Item6 * t.Item6), TupleUtil.Append) .SelectMany(t => Enumerable.Range(0, t.Item7 * t.Item7), TupleUtil.Append); foreach (var i in values) { Console.WriteLine("{0} {1} {2} {3}", i.Item1, i.Item2, i.Item3, i.Item4, i.Item5, i.Item6, i.Item7, i.Rest); }
これ以上は…考えないようにしよう。
*1:特にメソッド構文のほうが優れてるとかそういうのを言いたいわけではなく、メソッド構文でも楽な書き方って無いかなと考えて思いついただけです