C#でProject Euler(4) クエリ式

前回はPython

N = 1000
print sum(ifilter(lambda n: n % 3 == 0 or n % 5 == 0, xrange(1, N)))

C#で書くと、

        const int   N = 1000;
        var s = Enumerable.Range(1, N - 1).
                        Where(n => n % 3 == 0 || n % 5 == 0).Sum();
        Console.WriteLine(s);

となるのを見ました。このメソッドを連結する方法以外に、もう一つ方法があります。それがクエリ式を使うものです。具体的に見てみましょう。

        const int   N = 1000;
        var q = from n in Enumerable.Range(1, N - 1)
                where n % 3 == 0 || n % 5 == 0
                select n;
        Console.WriteLine(q.Sum());

from以下がクエリ式です。Pythonで言う内包表記というかジェネレータ式みたいなもんですね。forの代わりにfromで、他はメソッドを小文字にしたもの。最後にselectが要ります。だいたいSQL文みたいになっています。

不思議とメソッドを連結する形式より見やすくなっています。クエリ式が最も強力だと思うのは、複数の列を一つの列にするときですね。1〜5同士の積を列挙するとき、メソッドを連結する方法だとフラットにするメソッドSelectManyを使って、

        var g = Enumerable.Range(1, N).SelectMany(
                    x => Enumerable.Range(1, N).Select(y => x * y));
        foreach(var p in g) {
            Console.WriteLine(p);
        }

こんな感じだと思うのですが、クエリ式だと、

        var g = from x in Enumerable.Range(1, N)
                from y in Enumerable.Range(1, N)
                select x * y;
        foreach(var p in g) {
            Console.WriteLine(p);
        }

実にわかりやすくなります。Python

        g = (x * y for x in xrange(1, N + 1)
                   for y in xrange(1, N + 1))

と同じです。
Pythonと違うのはlet句が使えることです。

        var g = from x in Enumerable.Range(1, N)
                let s = Math.Sin((double)x)
                select s * s + 1;

クエリ式内で一時的な変数を使うことができます。Pythonではこれができません。Pythonは、内包表記が少し複雑になったら内部関数を作るという方針なのです。