読者です 読者をやめる 読者になる 読者になる

VB.NETだってLinqしたい

VB.NET

Linqとは

Linq(統合言語クエリ)とはとかって書こうと思ったけど、どうせWikiなり他のサイトにたっくさん書いてあるんでまぁ書かなくても良いかなとおもいました。

いくつか種類のあるLinqですが、ここでは Linq to Object(コレクションに関する操作)について紹介できたらこれ幸いです。

一番単純なクエリ

まずは、配列の要素をそのまま出力するコードを見てみましょう。

Dim numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Dim allNumber = From n In numbers
                Select n

For Each it In allNumber
    Console.WriteLine(it)
Next

メソッド構文で表すと以下のコードになります。

Dim allNumber = numbers.Select(Function(n) n)

Selectですが、これはSQLでのSELECTと同義と考えていいでしょう。 引数として受け取ったラムダ式で配列内の各要素をどのように出力するのか定義をしています。 ここでは受け取った要素をそのまま返しています。 以下のようにすれば各要素を二乗して返すことができます。

' クエリ構文
Dim allNumber = From n In numbers
                Select n * n

' メソッド構文
Dim allNumber = numbers.Select(Function(n) n * n)

いまさらっとラムダ式を使いましたが、ラムダ式とはなんでしょうか。 一言でサクッと誤解を恐れずに言ってしまうと『匿名デリゲートをなんか簡単かつ見た目きれいに作れるなんか便利なやつ』と言えるでしょう。 今私の事を『関数型言語では~』や『式ツリーでは~』という言葉で殴ろうとしているあなた。やめてくださいしんでしまいます。

えっ、『デリゲートってなんだ?』。 これまた誤解を恐れず言うと『クラスメソッドインスタンスメソッドを変数につっこんだり、ほかのメソッドに渡したりメソッドの返り値として受け取れる便利なやつ』です。 デリゲートを日本語に訳すと委譲と訳されることが多いです。

今回のクエリを例にすると、Selectを作ったマイクロソフトの技術者はユーザ(実際のプログラムをかくプログラマのこと)がSelectがどんな値を返してほしいか作成時は知りようがありません。 単に受け取った値をそのまま返せばいいのか、はたまた二倍にして返してほしいのか。 そこで、ユーザが実際に使う時にどんな値を返してほしいかを定義した式を外部から与えてやればいいのです。 この時、わざわざメソッドを宣言するのが面倒なのでその場でラムダ式として匿名デリゲートを与えています。

説明がなんかこれじゃない感がただよっているのですが、今回のメインテーマはLinqの導入なのでという言い訳で逃げようと思います。

フィルタリング

Linqにはいろいろな操作があります。 たとえば、先ほどの配列の数値のうち偶数のみを表示させてみましょう。

Dim numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

Dim evenNumber = From n In numbers
                 Where n Mod 2 = 0
                 Select n

For Each it In evenNumber
    Console.WriteLine(it)
Next
Dim evenNumber = numbers.Where(Function(n) n Mod 2 = 0).Select(Function(n) n)

さきほどと違う点はSelectの前にWhereが追加されています。 Whereに要素を引数にとりフィルタリングを通す場合はTrue、通さない場合はFalseを返すデリゲートを指定します。 この場合はラムダ式でフィルタ条件を指定していますね。

さらに条件を付けくわえて5以下の数字を表示させたい場合は以下のように変更すればOKです。

Dim evenNumber = From n In numbers
                 Where n <= 5
                 Where n Mod 2 = 0
                 Select n
Dim evenNumber = numbers.
    Where(Function(n) n Mod 2 = 0).
    Where(Function(n) n <= 5).
    Select(Function(n) n)

このように望む操作をメソッドチェーンで付け加えて最後にSelectすれば欲しい結果が手に入る。便利な世の中になりましたね。 他にも集計したりソートしたりといろいろなことができるのですが、のちのち紹介していきたいです。

番外 JavaでもStreamしたい

JavaでもJava8でLinq to Objectに似たStream APIが追加されたのは記憶に新しいですね。

JavaのStreamは.NETのように拡張メソッドで機能が提供されるのではなくStreamインスタンスを生成しそのStreamインスタンスに対して操作をメソッドチェーンで付け加える形になります。

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Arrays.stream(numbers)
        .filter(ex -> ex % 2 == 0)
        .filter(ex -> ex <= 5)
        .forEach(System.out::println);

中間操作として偶数と5以下の数でフィルタンリングを行い、終端操作としてforEachメソッドにprintlnメソッドメソッド参照で渡して表示を行っています。 一気に書くとなんか呪文みたいですが、やりたいことは上のVBのコードと同じです。

Javaたのしい