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

VB.NETでも部分適用された関数を返す関数を書きたい

VB.NET

部分適用された関数

私は関数型プログラミングについてそこまで精通しているわけではないので間違ったことを言っちゃうかもです。

って前置きをしておいてからの本題ですが、今書いているプログラムであらかじめ値を適用された関数を受け取り、その関数をすぐには使わずにあとでグヘヘしたいわけなんです。 でもってその適用する値がプログラムの作成時にはわからず実行時に決定するので単なるラムダではだめなのです。 つまり、適用したい値を引数にとりその値が適用された関数を返す関数を書きたいのです。

簡単な例で言いますと、たとえば与えられた数がある別に与えられる範囲に収まっているかを判別する関数を書くとします。 つまり以下のような関数を書く必要がありますね。

(判定する値、下限値、上限値)-> 真偽値

これを

(下限値、上限値)-> (判定する値)-> 真偽値

こうしたい訳なのです。

僕らの最強純粋関数型言語Haskellならカリー化関数のご加護より勝手にそうなるのですが、残念ながら我々が書いているのはVB.NETです。 しかしVB6.0ではないのでまだ望みはあります。

オブジェクト指向のぬるま湯につかっている我々が思いつく最初の方法が検査を行うメソッドを一つ定義したインターフェースを用意しそれを実装するクラスを作成しコンストラクタで下限値・上限値を指定する方法ですね。これならクラスがいくつ増えようが同じインターフェースで検査を行うことができます。

Option Strict On

Class HelloWorld
    Public Shared Sub Main(ByVal args As String())
        Dim v As IValidator = New RangeValidator(0, 100)
        Console.WriteLine(v.Validation(50))
    End Sub
End Class

Interface IValidator
    Function Validation(value As Integer) As Boolean
End Interface

Class RangeValidator
    Implements IValidator

    Private min As Integer
    Private max As Integer

    Public Sub New(min As Integer, max As Integer)
        Me.min = min
        Me.max = max
    End Sub

    Public Function Validation(value As Integer) As Boolean _
        Implements IValidator.Validation
        Return min <= value And value <= max
    End Function
End Class

割といい感じです。ただ、すごくめんどう。 この方法ですと、判別関数が一つ増えるたびにクラスが一つ増えます。

ラムダパワー

上のコードと等価なコードをラムダを使って書いてみましょう。

Option Strict On

Class HelloWorld
    Public Shared Sub Main(ByVal args As String())
        Dim rangeValidatorGenerator = _
            Function(min As Integer, max As Integer)
                Return Function(value As Integer)
                           Return min <= value And value <= max
                       End Function
            End Function

        Dim v = rangeValidatorGenerator(0, 100)
        Console.WriteLine(v(50))
        Console.ReadLine()
    End Sub
End Class

上記のコードより短くなりました。 外側の関数に引数として渡された上限値及び下限値を内側の関数が持ち運び、必要なときに条件として使用しています。

ただ、クッソ分かりにくくなるので、多用は避けた方がよさそうです。