2011年11月9日 星期三

#VB Lambda


Lambda 表达式 Lambda Expressions (Visual Basic)


http://www.cosdiv.com/page/M0/S225/225236.html



====================================================

  • Lambda 運算式 (Visual Basic)



    Lambda 運算式沒有名稱。

  • Lambda 運算式不能有修飾詞 (Modifier),例如 Overloads 或 Overrides

  • 單行 Lambda 函式不會使用 As 子句來指定傳回型別。 而是從 Lambda 運算式評估之主體的值來推斷型別。 例如,如果 Lambda 運算式的主體是 cust.City = "London",其傳回型別為Boolean

  • 在多行 Lambda 函式中,您可以使用 As 子句來指定傳回型別,也可以省略 As 子句,使傳回型別成為推斷型別。 省略多行 Lambda 函式的 As 子句時,傳回型別將推斷為多行 Lambda 函式中所有 Return 陳述式的主型別。 主型別」(Dominant Type) 是一個唯一的型別,提供給Return 陳述式的其他所有型別都可以擴展至該型別。 如果無法決定這個唯一型別,主型別就成為提供給 Return 陳述式的其他所有型別可以縮小至的唯一型別。 如果無法決定所有這些型別,則主型別為 Object


    例如,如果提供給 Return 陳述式的運算式含有型別值 IntegerLong 和 Double,則產生列的型別是 Double Integer 和 Long 都會擴展為 Double 而且僅限為 Double 因此,Double 是主型別。 如需詳細資訊,請參閱擴展和縮小轉換 (Visual Basic)

  • 單行函式的主體必須是可以傳回值的運算式,而不是陳述式。 單行函式沒有 Return 陳述式。單行函式傳回的值就是函式主體中運算式的值。

  • 單行副程式的主體必須是單行陳述式。

  • 單行函式和副程式不包含 End Function 或 End Sub 陳述式。

  • 您可以使用 As 關鍵字來指定 Lambda 運算式參數的資料型別,也可以推斷參數的資料型別。所有參數都必須具有指定的資料型別,不然所有參數就都必須經過推斷。

  • 不允許 Optional 和 Paramarray 參數。

  • 不允許使用泛型參數。
===================================================
做隱含型別轉換時,需要注意是否符合相同簽章
<Function(Integer) As Boolean>



   '隱含型別轉換測試
    '<Function(Integer) As Boolean>
    Delegate Function MultipleOfTen(ByVal num As Integer) As Boolean


    '接受委派的方法
    Function IsMultipleOfTen(ByVal num As Integer) As Boolean
        Return num Mod 10 = 0
    End Function

    ' This method takes an input parameter of the delegate type. 
    ' The checkDelegate parameter could also be of 
    ' type Func(Of Integer, Boolean).
    Sub CheckForMultipleOfTen(ByVal values As Integer(),
                              ByRef checkDelegate As MultipleOfTen)
        For Each value In values
            If checkDelegate(value) Then
                Console.WriteLine(value & " is a multiple of ten.")
            Else
                Console.WriteLine(value & " is not a multiple of ten.")
            End If
        Next
    End Sub


    Sub CheckValues()
        Dim values = {5, 10, 11, 20, 40, 30, 100, 3}
        '委派方法
        'CheckForMultipleOfTen( Integer(), MultipleOfTen )
        '會將 MultipleOfTen 交給 IsMultipleOfTen() 去做
        'checkDelegate 被 IsMultipleOfTen 所取代
        CheckForMultipleOfTen(values, AddressOf IsMultipleOfTen)
        'Lambda
        '直接帶入 Lambda, checkDelegate 會被 Lambda 取代
        'Lambda 隱含轉換為 <Function(Integer) As Boolean>
        CheckForMultipleOfTen(values, Function(num) num Mod 10 = 0)
    End Sub



透過 Function 叫用 Lambda




        ' Create a DataTable to store the data.
        'Closure 自由變數
        Dim valuesTable = New DataTable("Calculations")
        valuesTable.Columns.Add("Value", GetType(Double))
        valuesTable.Columns.Add("Calculation", GetType(String))
        valuesTable.Columns.Add("Result", GetType(Double))

        ' Define a lambda subroutine to write to the DataTable.
        Dim writeToValuesTable = Sub(value As Double, calcType As String, result As Double)
                                     '每次叫用,將為自由變數 valuesTable
                                     '新增一筆資料
                                     Dim row = valuesTable.NewRow()
                                     row(0) = value
                                     row(1) = calcType
                                     row(2) = result
                                     valuesTable.Rows.Add(row)
                                 End Sub

        ' Define the source values.
        Dim s = {1, 2, 3, 4, 5, 6, 7, 8, 9}

        ' Perform the calculations.
        '第二個參數透過方法(帶入Lambda方法)叫用
        '感覺 sub(c) 是取 S 的元素來帶入,用法太高接了~
        'Array.ForEach(Of T) 
        '看起來 S 元素為 Integer, 所帶入的 c 參數會被視為元素的 型別
        '跟著 Sub(c) 走,後面func就要使用 c 為參數
        Array.ForEach(s, Sub(c) CalculateSquare(c, writeToValuesTable))
        Array.ForEach(s, Sub(c) CalculateSquareRoot(c, writeToValuesTable))

        ' Display the data.
        Console.WriteLine("Value" & vbTab & "Calculation" & vbTab & "Result")
        For Each row As DataRow In valuesTable.Rows
            Console.WriteLine(row(0).ToString() & vbTab &
                              row(1).ToString() & vbTab &
                              row(2).ToString())
        Next

   '委派方法型別
    Delegate Sub StoreCalculation(ByVal value As Double,
                       ByVal calcType As String,
                       ByVal result As Double)
    '平方
    Sub CalculateSquare(ByVal number As Double, ByVal writeTo As StoreCalculation)
        '叫用Lambda
        writeTo(number, "Square     ", number ^ 2)
    End Sub

    '開平方根
    Sub CalculateSquareRoot(ByVal number As Double, ByVal writeTo As StoreCalculation)
        '叫用Lambda
        writeTo(number, "Square Root", Math.Sqrt(number))
    End Sub




可為 Null 的實值型別 (Visual Basic)





Dim ridesBusToWork1? As Boolean
Dim ridesBusToWork2 As Boolean?
Dim ridesBusToWork3 As Nullable(Of Boolean)

        '讓 function 可接受 Null 的實質型別 (參數不具有指派值)
        '在後面才能判斷 該 實質變數 is or isnot nothing
        '或是透過 isnothing(變數) 去判斷
        ' (不可以直接用 = nothing 去判斷,會將變數指定為nothing)
        'notNothing, notNothing2 將從委派取得型別 Boolean
        Dim notNothing =
          Function(num As Integer) Not IsNothing(num)
        Dim notNothing2 =
            Function(num? As Integer) num IsNot Nothing
        Dim arg As Integer = 14
        Console.WriteLine("Does the argument have an assigned value?")
        Console.WriteLine(notNothing(arg))
        Console.WriteLine("Does the argument have an assigned value?")
        Console.WriteLine(notNothing2(arg))


===================================================

'巢狀測試
        '設定 Prop 屬性
        Dim lambdaScopeDemoInstance =
            New LambdaScopeDemoClass With {.Prop = 1}


        '叫用含有Lambda的Function,將改寫 Closure ,
        '並初始化 Closure 的自由變數(第一次進來的時候發生)
        '設定最外層functionWithNestedLambda(level1 = 2)
        '在functionWithNestedLambda之下在一次 叫用Lambda(3),讓 level2 = 3
        Dim aDel As aDelegate =
            lambdaScopeDemoInstance.functionWithNestedLambda(2)


        '再叫用 Lambda(4),讓 level3 = 4
        Console.WriteLine("First value returned by aDel:   " & aDel(4))


        'Closuer外的區域變數
        lambdaScopeDemoInstance.aField = 20
        lambdaScopeDemoInstance.Prop = 30

        '之後再次叫用,只會設定最內層 Lambda (Level3)
        '所以外層設定後,就不法再被更動,剩下最內層繫結變數可以控制 (或是Closure外的區域變數)
        Console.WriteLine("Second value returned by aDel: " & aDel(40))







        Public Function functionWithNestedLambda(
            ByVal level1 As Integer) As aDelegate

            Dim localVar As Integer = 5

            ' When the nested lambda expression is executed the first 
            ' time, as aDel from Main, the variables have these values:
            ' level1 = 2
            ' level2 = 3, after aLambda is called in the Return statement
            ' level3 = 4, after aDel is called in Main
            ' locarVar = 5
            ' aField = 6
            ' aProp = 1
            ' The second time it is executed, two values have changed:
            ' aField = 20
            ' aProp = 30
            ' level3 = 40
            Dim aLambda = Function(level2 As Integer) _
                              Function(level3 As Integer)
                                  '當所有繫結變數都被復職以後,
                                  '才做Lambda傳回值的動作
                                  '之後的叫用都只能設定最內層 Function( Level3 ) 的數值
                                  Return level1 + level2 + level3 + localVar +
                                                                       aField + aProp
                              End Function


            ' The function returns the nested lambda, with 3 as the 
            ' value of parameter level2.
            Return aLambda(3)
        End Function





===================================================
1. 對使用 Lambda 的方法, 改寫為 closure 類別
    將使用 Lambad 的方法中的區域變數, 視為 Closure 的自由變數
    宣告以後就將該自由變數做初始化
2. 將 Lambda 方法, 寫入 Closure 下
3. Lambda 中的繫結變數, 等待使用時傳入參數,去執行Lambda的事情
4. 所以自由變數在宣告後, 仍然不會被回收, 直到確定Lambda不會再被使用為止




 '委派方法型別,使用上須帶入一個參數,傳回Boolean
    Delegate Function gameDelegate(ByVal aGuess As Integer) As Boolean


    '被委派的方法(不用寫參數),傳回gameDelegate方法型別 < Function(Integer) As Boolean >
    Public Function makeTheGame() As gameDelegate

        '初始化並產生亂數
        Randomize()
        Dim target As Integer = CInt(Int(10 * Rnd() + 1))

        Console.WriteLine("(Peeking at the answer) The target is " & target)

        ' Does the guess equal the target?
        'Lambda 判斷數字是否相同
        'Dim playTheGame = Function(guess As Integer) guess = target


        Dim playTheGame = Function(guess As Integer)
                              Return guess = target
                          End Function


        '< Function(Integer) As Boolean > 
        Return playTheGame

    End Function



==================================================

茅塞頓開的一晚-Func 委派+匿名方法+lambda



感覺 func 適合放在參數列中
去把 function 帶入當作一個參數使用

而不適合直接在外面宣告使用
有點多此一舉
兩個做到的是一樣的


        Dim chk As Func(Of Motorcycle, Boolean) = Function(t As Motorcycle) t.color = "Red" And _
                                           t.CC = 600 And _
                                           t.weight > 300 And _
                                           t.weight < 400


        Dim chk  = Function(t As Motorcycle) t.color = "Red" And _
                                           t.CC = 600 And _
                                           t.weight > 300 And _
                                           t.weight < 400



帶入function作為參數
參數宣告就要使用 func


    Sub Main()
        ' The following line will print Success, because 4 is even.
        testResult(4, Function(num) num Mod 2 = 0)
        ' The following line will print Failure, because 5 is not > 10.
        testResult(5, Function(num) num > 10)
    End Sub

    ' Sub testResult takes two arguments, an integer value and a 
    ' Boolean function. 
    ' If the function returns True for the integer argument, Success
    ' is displayed.
    ' If the function returns False for the integer argument, Failure
    ' is displayed.
    Sub testResult(ByVal value As Integer, ByVal fun As Func(Of Integer, Boolean))
        If fun(value) Then
            Console.WriteLine("Success")
        Else
            Console.WriteLine("Failure")
        End If
    End Sub



===============================
太怪了~
測試可以
在全限控管那邊卻一直說無法轉匿名方法為boolean
沒跟想像中自動推斷型別

Dim check = Function(m As Motorcycle) m.color = "Red" And _
                                              m.CC = 600 And _
                                              m.weight > 300 And _
                                              m.weight < 400
        If check(motorcycle) Then
            Console.WriteLine("成功")

            ' do something here
        End If

        ' do something here

        If Not check(motorcycle) Then
            Console.WriteLine("失敗")
            ' do something here
        End If



'下面宣告方式 是錯誤的
在外層宣告物件m2
用在 Lambda 之下,
感覺是因為 m2 是屬於 Lambda的繫結變數
編譯器改寫的時候該變數只屬於 Lambda
而不會視為是自由變數

不過反映出來的錯誤訊息是
匿名型別無法轉換為 Boolean

所以可能要判斷某些東西,可能一定要透過參數去繫結變數
來做判斷動作


        Dim chk = Function() m2.color = "Red"

        If chk Then  ->抱錯

        End If




=======================================================


VB當中的新語法(四) - Lambda



Lambda 運算式
http://msdn.microsoft.com/zh-tw/magazine/cc163362.aspx

1. 看起來可以透過Lambda的推斷型別功能
去建立一些重複性檢查的Helper工具

把該Lambda作為function呼叫使用
避免重複性的撰寫程式碼



2.Lambda 運算式和變數提取 (Variable Lifting)
在先前的範例中,Lambda 運算式主體會參考傳入 Lambda 的變數。然而,Lambda 運算式的真正威力在於其變數提取功能。如我先前所提示,編譯器會在某些情況下「實現神奇結果」。在探討這些情況之前,讓我們先了解一些數學領域分支 (稱為 Lambda 微積分) 的基本概念,因為 Lambda 運算式採用的概念很類似。
Lambda 微積分的基本概念,就是函式可以有自由變數或繫結變數。自由變數是定義於包含方法之變數中的變數 (區域變數和參數)。繫結變數是定義於 Lambda 簽名碼中的變數,或是屬於包含 Lambda 之類別 (包括基底類別) 的成員。
您必須在 Lambda 運算式中清楚區分繫結變數和自由變數,這一點很重要,因為它們會影響 Lambda 運算式的語意、所產生的程式碼,以及最終會影響程式的正確性。下列為包含繫結和自由變數的 Lambda 運算式範



Dim y As Integer = 10
Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
在此處,x 是 Lambda 內的繫結變數,因為它是 Lambda 運算式的型式參數,而 y 則是自由變數,因為它是屬於 Lambda 運算式之包含方法的變數。


3.宣告Lambda以後,將自動建立他的委派型別
   所以可以將Lambda運算式 作為 回傳型別來使用


Function MakeLambda() As Func(Of Integer, Integer)
    Dim y As Integer = 10
    Dim addTen As Func(Of Integer, Integer) = Function(ByVal x) x + y
    Return addTen
End Function

Sub UseLambda()
    Dim addTen = MakeLambda()
    Console.WriteLine(addTen(5))
End Sub
y在此視為區域變數,
所以應該在 Dim addTen = MakeLambda()之後
y就應該從堆疊中消失
但是沒有

當編譯器發現 Lambda 運算式中有自由變數時,就會將該自由變數提取到稱為 closure 的類別中。closure 的存留期會超越提取到其中之自由變數的存留期。編譯器會在方法中重新撰寫變數的存取碼,改為存取在 closure 執行個體內的變數。


也就是他會建立一個 closure 類別
將 自由變數 Y 作為他的區域變數
並且在類別中重新撰寫呼叫使用Lambda 
( 即便是在使用上改變了自由變數的數值,將會透過 closure.Y 的呼叫方式重新叫用
所以會直接的影響到自由變數的數值 )


4. Dim x = If(condition, 10, 20)
If 關鍵字與 IIF 函式的呼叫相似,但是 If 關鍵字有完全的型別安全性。這意味著在上述範例中,編譯器推算 If 關鍵字的兩個分支都會傳回整數,所以就會套用型別推斷規則並決定 x 的型別為 Integer。如果使用 IIF,x 的型別就會是 Object。

不同的是,if 三元運算,會加入型別推斷功能


Threading.Tasks.Parallel

沒有留言:

張貼留言