11

パラメーターを備えたメソッド呼び出しを取得し、その時点でパラメーターを指定しなくても、それらのパラメーターで指定された関数を呼び出す MethodInvoker に変換すると便利な場合があります。他の場合には、同様のことを行うと便利ですが、いくつかのパラメーターは開いたままにします。このタイプのアクションは「カリー化」と呼ばれます。VBでこれを行うための最良のパターンは何ですか?

VB 2010 でラムダ式を使用することは可能ですが、ラムダ式はエディット コンティニュと互換性がなく、ラムダ式が作成するクロージャーは参照による予期しない動作をする可能性があります。別のアプローチは、次に示すようないくつかの一般的なメソッドを定義することです。

Public Module CurryMagic
    Delegate Sub Action(Of T1, T2)(ByVal P1 As T1, ByVal P2 As T2)
    Delegate Sub Action(Of T1, T2, T3)(ByVal P1 As T1, ByVal P2 As T2, ByVal P3 As T3)

    Class CurriedAction0(Of FixedType1, FixedType2)
        Dim _theAction As Action(Of FixedType1, FixedType2)
        Dim _FixedVal1 As FixedType1, _FixedVal2 As FixedType2
        Sub Exec()
            _theAction(_FixedVal1, _FixedVal2)
        End Sub
        Sub New(ByVal theAction As Action(Of FixedType1, FixedType2), _
                ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2)
            _theAction = theAction
            _FixedVal1 = FixedVal1
            _FixedVal2 = FixedVal2
        End Sub
    End Class

    Class CurriedAction1(Of ArgType1, FixedType1, FixedType2)
        Dim _theAction As Action(Of ArgType1, FixedType1, FixedType2)
        Dim _FixedVal1 As FixedType1, _FixedVal2 As FixedType2
        Sub Exec(ByVal ArgVal1 As ArgType1)
            _theAction(ArgVal1, _FixedVal1, _FixedVal2)
        End Sub
        Sub New(ByVal theAction As Action(Of ArgType1, FixedType1, FixedType2), _
                ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2)
            _theAction = theAction
            _FixedVal1 = FixedVal1
            _FixedVal2 = FixedVal2
        End Sub
    End Class

    Class ActionOf(Of ArgType1)
        Shared Function Create(Of FixedType1, FixedType2)(ByVal theSub As Action(Of ArgType1, FixedType1, FixedType2), ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2) As Action(Of ArgType1)
            Return AddressOf New CurriedAction1(Of ArgType1, FixedType1, FixedType2)(theSub, FixedVal1, FixedVal2).Exec
        End Function
    End Class

    Function NewInvoker(Of FixedType1, FixedType2)(ByVal theSub As Action(Of FixedType1, FixedType2), ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2) As MethodInvoker
        Return AddressOf New CurriedAction0(Of FixedType1, FixedType2)(theSub, FixedVal1, FixedVal2).Exec
    End Function
End Module

Foo(5, "Hello") を実行する MethodInvoker を作成したい場合は、次を使用して作成できます。

MyInvoker = NewInvoker(AddressOf Foo, 5, "Hello")

MyAction(X) を Boz(X, "George", 9) (X は Double) に変換したい場合は、

MyAction = ActionOf(Of Double).Create(AddressOf Boz, "George", 9)

さまざまな数の固定および非固定パラメーターに対応するために膨大な量の定型コードが必要であり、どのパラメーターが固定され、どのパラメーターが固定されていないかを明確にするデリゲート作成構文に固有のものがないことを除いて、すべて非常に巧妙です。修繕。パターンを改善する方法はありますか?

補遺:デリゲートが構造体メンバー関数から作成された場合のメカニズムは何ですか? デリゲートは構造体の独自のコピーを取得しているようですが、そのコピーがボックス化されているかボックス化されていないかはわかりません。ボックス化されていない場合、CurryAction0 と CurryAction1 を構造体に置き換えると、デリゲートの作成時に CurryAction0 または CurryAction1 を別のヒープ オブジェクトとして割り当てる必要がなくなります。ただし、ボックス化する場合、構造体を使用すると、何も保存せずに構造体をボックス化されたインスタンスにコピーするというオーバーヘッドが追加されます。

4

4 に答える 4

1

.Net 4 を使用できる場合、タプルはどうですか?

    ''Create new tuple instance with two items.
    Dim tuple As Tuple(Of Integer, String) = _
        New Tuple(Of Integer, String)(5, "Hello")
    ''Now you only have one argument to curry, packaging both parameters
    ''Access the parameters like this (strongly typed)
    Debug.Print tuple.Item1 '' 5
    Debug.Print tuple.Item2 '' "Hello"
于 2010-12-23T10:03:27.227 に答える
0

ContiniousLinqの機能を確認してください。テンプレートを使用して、すべてのカレー関数を自動生成します。

https://github.com/ismell/Continuous-LINQ/blob/master/ContinuousLinq/Expressions/Curry.tt

その結果、

https://github.com/ismell/Continuous-LINQ/blob/master/ContinuousLinq/Expressions/Curry.cs

たぶん、テンプレートを取得して変更し、VBを生成することができますか?

ラウル

于 2011-12-30T18:58:10.093 に答える
0

C# 4.0 についてこれを尋ねられたら、動的型を使用してください。

しかし面白いことに、VB は Option Strict をオフにすると、常に動的型付けをサポートしていました。

定型コードの量が多すぎるのを避けるために、可変数のパラメーターを使用してオーバーロードを作成できるかどうかを確認してみてください。遅くなりますが、コードがどの関数でも機能することを確認するための便利なセーフティ ネットです。

おそらく実装の詳細としてクロージャーが必要になると思いますが、それは問題ありませんか?

于 2012-01-22T21:27:31.553 に答える
0

これは、考えられるすべての「遅延」引数のボイラープレート要件を回避するものではありませんFuncが、「単純な」アプローチが依然としてかなりクリーンであることを示したいだけです。VB は、便利な構造のように見えるため、少し冗長です。

また、現在の定義は、呼び出しで型が明示的に指定されてCurryいないと暗黙のうちに機能しません:-(Of

編集:暗黙的なオプションが明示的なFunc変数に対して機能することを示します。

 Option Explicit On 
 Option Strict On
 Option Infer On

 Imports System
 Imports Microsoft.VisualBasic

 Module CurryTest

 Function Test1(ByVal X As String, ByVal Y As String) As String
   Return X & Y
 End Function

 Function Test2(ByVal X As Integer, ByVal Y As Integer) As Integer
   Return X + Y
 End Function

 Function Test3(ByVal X As Integer, ByVal Y As Integer, ByVal Z As String) As String
   Return Z & ":" & CStr(X + Y)
 End Function

 Sub Main()

   Dim Curry1 = Curry(Of String, String, String)(AddressOf Test1, "a")
   Dim Curry2 = Curry(Of Integer, Integer, Integer)(AddressOf Test2, 2)
   Dim Curry3 = Curry(Of Integer, Integer, String, String)(AddressOf Test3, 1, 2)

   Dim f As Func(Of String, String, String) = AddressOf Test1
   Dim g As Func(Of Integer, Integer, Integer) = AddressOf Test2
   Dim h As Func(Of Integer, Integer, String, String) = AddressOf Test3

   Dim Curry4 = Curry(f, "b")
   Dim Curry5 = Curry(g, 3)
   Dim Curry6 = Curry(h, 4, 5)

   Console.WriteLine(Curry1("b"))
   Console.WriteLine(Curry1("c"))

   Console.WriteLine(Curry2(2))
   Console.WriteLine(Curry2(3))

   Console.WriteLine(Curry3("Three"))
   Console.WriteLine(Curry3("3 "))

   Console.WriteLine(Curry4("c"))
   Console.WriteLine(Curry4("d"))

   Console.WriteLine(Curry5(4))
   Console.WriteLine(Curry5(5))

   Console.WriteLine(Curry6("Nine"))
   Console.WriteLine(Curry6("9 "))

 End Sub

 Function Curry(Of T, U, V)(ByVal Fn As Func(Of T, U, V), ByVal Arg As T) As Func(Of U, V)
   Return Function(Arg2 As U)(Fn(Arg,Arg2))
 End Function

 Function Curry(Of T, U, V, W)(ByVal Fn As Func(Of T, U, V, W), ByVal Arg1 As T, ByVal Arg2 As U) As Func(Of V, W)
   Return Function(Arg3 As V)(Fn(Arg1,Arg2,Arg3))
 End Function

 End Module
于 2011-06-20T14:56:18.953 に答える