VBで書くラムダ式

LINQはうまく使えば非常に便利なのですが、残念ながらVBで書くと非常にゴチャゴチャします。C# なら => で済む所が、Function やら Sub やら長ったらしく、しかも使い分ける必要があるのが悩みどころです。どういう感じになるか、ひとつやってみます。

お題:小田急線の列車種別・線別の停車駅をクエリして、コンソールに出力します。「正解」はコチラです。

Step1: 駅名を保持するクラスを用意します。列車種別と線名にFlags属性が付いているところに注意してください。

    Private Class Station

        <Flags()>
        Friend Enum TrainKind
            各停 = 1
            区間準急 = 2
            準急 = 4
            多摩急行 = 8
            急行 = 16
            快速急行 = 32
            特急 = 64
        End Enum

        <Flags()>
        Friend Enum Line
            小田原線 = 1
            多摩線 = 2
            江ノ島線 = 4
        End Enum

        Property 駅名 As String
        Property 線 As Line
        Property 停車 As TrainKind

    End Class

Step2:上で作ったクラスに初期値を入れておきます。 Sub main の冒頭でやっておけば十分です。(全駅を書くと大変なので、一部だけにします。)

        Dim colStations As New List(Of Station)
        With colStations
            .Add(New Station With {.駅名 = "代々木上原", .線 = Station.Line.小田原線, .停車 = Station.TrainKind.多摩急行 Or Station.TrainKind.快速急行 Or Station.TrainKind.急行 Or Station.TrainKind.準急 Or Station.TrainKind.区間準急 Or Station.TrainKind.各停})
            .Add(New Station With {.駅名 = "豪徳寺", .線 = Station.Line.小田原線, .停車 = Station.TrainKind.区間準急 Or Station.TrainKind.各停})
            .Add(New Station With {.駅名 = "登戸", .線 = Station.Line.小田原線, .停車 = Station.TrainKind.多摩急行 Or Station.TrainKind.急行 Or Station.TrainKind.準急 Or Station.TrainKind.区間準急 Or Station.TrainKind.各停})
            .Add(New Station With {.駅名 = "向ヶ丘遊園", .線 = Station.Line.小田原線, .停車 = Station.TrainKind.特急 Or Station.TrainKind.急行 Or Station.TrainKind.準急 Or Station.TrainKind.区間準急 Or Station.TrainKind.各停})
            .Add(New Station With {.駅名 = "栗平", .線 = Station.Line.多摩線, .停車 = Station.TrainKind.急行 Or Station.TrainKind.多摩急行 Or Station.TrainKind.区間準急 Or Station.TrainKind.各停})
            .Add(New Station With {.駅名 = "南林間", .線 = Station.Line.江ノ島線, .停車 = Station.TrainKind.急行 Or Station.TrainKind.各停})
        End With

Step3:早速クエリを作ってみます。条件は「小田原線内の多摩急行停車駅」です。まずは、一行で条件を書いてみます。

        Dim queryInLine = colStations.Where(Function(x) (x.停車 And Station.TrainKind.多摩急行) AndAlso (x.線 And Station.Line.小田原線)) _
                            .Select(Function(y) New With {.result = y.駅名 & "[" & y.線.ToString() & "]"})

        For Each stationName In queryInLine
            Console.WriteLine(stationName.result)
        Next

一行で書こうとするとやはりゴチャゴチャします。
このうちwhereの部分だけををC# で書くとすると、

var query = colStations.Where(x => (x.停車.HasFlag(Station.TrainKind.多摩急行)) && (x.線.HasFlag(Station.Line.小田原線)));

と、かなりすっきりするのですが、ないものねだりをしても仕方がありません。

また、Selectで匿名クラスを作っています。匿名クラスのメンバーであろうと、インテリセンスは補完してくれます。

Step4:同じ条件で、複数行で書いてみます。VS2010からは「暗黙の行継続文字」という機能がついたので、狂ったように”_”を付けなくていいところが救いです。「暗黙の行継続文字」が適用される条件については、コチラをご覧下さい。

        Dim queryWithLines = colStations.Where(Function(x)
                                                   Return (x.停車 And Station.TrainKind.多摩急行) _
                                                            AndAlso _
                                                          (x.線 And Station.Line.小田原線)
                                                   End Function
                                ).ToArray()


        Array.ForEach(queryWithLines, Sub(station As Station)
                                      Console.WriteLine(String.Format("{0} [{1}]", station.駅名, station.線))
                                  End Sub)

確かに冗長ですが、見通しはかなり良くなりました。

なお、Array.ForEachの中で直接Console.WriteLineを呼んでで見ましたが、ここは”Sub”であるところに注意してください。つまり、値を返さないので、”Function”ではないのです。この使い分けが若干面倒ではあります。

Step5:最後に、デリゲートを使ってみます。

                Dim fncFilter = Function(x As Station)
                            Return (
                                (x.停車 And Station.TrainKind.多摩急行) _
                                AndAlso _
                                (x.線 And Station.Line.小田原線))
                            End Function

        Dim fncWriteLine = Sub(x As Station)
                               Console.WriteLine(String.Format("{0} [{1}]", x.駅名, x.線))
                           End Sub

        Dim queryUseDeligate = colStations.Where(Function(x) fncFilter(x))
        Array.ForEach(queryUseDeligate.ToArray(), Sub(x As Station) fncWriteLine(x))

C#の「匿名メソッド」のようなものです(コチラによると厳密には違うそうですが)。ここでは細かいことは気にせず、「こういうやり方もありますよ」ということで。
デリゲートの変数名とか書く場所に気をつけてあげれば、見通しの良いコードになりそうな気がします。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Spam Protection by WP-SpamFree