2010-12-23 33 views
10

有時候採用一個方法調用,使用參數完成並將其轉換爲一個MethodInvoker,它將使用這些參數調用指定的函數,而無需在當時指定參數。在其他時候,做類似的事情是有用的,但是保留一些參數。這種行爲稱爲「Currying」。在VB中做這件事最好的模式是什麼?什麼是最好的模式來委託委託參數(使用.NET 2.0或更高版本)?

可以在VB 2010中使用lambda表達式,但lambda表達式與edit-and-continue不兼容,並且它們創建的閉包可能具有意想不到的引用行爲。另一種方法是定義一些通用的方法,如下所示:

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一個MethodInvoker(5,「你好」),我可以通過創建一個

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

,如果我想打開MyAction(X)爲博茲(X,「喬治」,9),其中X是一個雙重的,我可以用

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

所有非常漂亮,但它是必須有大量的樣板代碼以適應不同數量的固定參數和非固定參數,並且在委託創建語法中沒有任何固有的東西可以明確哪些參數是固定的,哪些參數是不固定的。有沒有辦法改善模式?

附錄: 如果從一個struct成員函數創建一個委託,機制是什麼?看起來委託人獲得了自己的結構副本,但我不知道該副本是裝箱還是取消裝箱。如果沒有裝箱,用結構替換CurryAction0和CurryAction1可以避免在創建代理時需要分配CurryAction0或CurryAction1作爲單獨的堆對象。但是,如果它將被裝箱,使用結構會增加複製結構到盒裝實例的開銷,而不保存任何東西。

+0

我不確定你想要什麼,但我用藍色射擊:您可以創建委託而不指定參數,然後通過Invoke方法將它們作爲Object-Array傳遞。 – Bobby 2010-12-23 08:21:05

+2

*'這種行爲稱爲「Currying」* - 實際上它是部分應用。 'Currying'是指某些語言的結構功能使其易於部分應用。 http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application – 2010-12-23 10:07:33

+0

@蒂姆羅賓遜:嗯......通過這種描述,柯里裏將是創建執行部分應用程序的方法的過程,與執行行爲不同應用程序本身?無論如何,我一直在尋找一種很好的方式來做到這一點。我的方法需要大量的樣板代碼才能工作,這很煩人,但避免了關閉的麻煩。順便說一句,代表結構裝箱或拆箱?如果取消裝箱,我的模式可以通過使用結構而不是類來改善,但是如果裝箱,這將是一種浪費。 – supercat 2010-12-23 16:04:13

回答

1

如果你可以使用.Net 4,tuples怎麼樣?

''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" 
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 
0

如果你要問我這個對C#4.0中,我會說:使用動態類型。

但有趣的是,如果你打開Option Strict Off,VB總是支持動態輸入。

爲了避免過多的樣板代碼,您可以嘗試查看是否可以使用可變數量的參數進行過載。它會更慢,但它是一個有用的安全網,以確保您的代碼適用於任何功能。

我想你可能需要閉包作爲實現細節,但沒關係,不是嗎?