2008年7月19日 星期六

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

這個世界上很多東西是沒有道理的...不知道你是不是也會這樣覺得。

老實說,過去寫程式寫慣了的人,一開始看到Lambda運算式會覺得怪怪的,我先講它的意義,官方說法是,Lambda運算式可以簡化程式碼的寫法、增加可讀性(老實說我覺得簡化程式碼的寫法這件事情很少跟增加可讀性可以一起發生,通常程式碼越簡化,可讀性就越差...),對Lambda運算式來說,簡化程式碼寫法可能是做到了,增加可讀性我就很猶豫了,不過如果真要說起來,Lambda運算式的出現(或是為什麼在這個版本出現)其實說穿了還是跟LINQ有關...

先寫一個簡單的Lambda:
Function(para1 As Integer) para1 + 1

MSDN上說,Lambda運算式是沒有名稱的函式,所以上面名稱不見了,return也不見了,其實你可以想成:
Function 函式名稱(para1 As Integer) Return para1 + 1
上面這樣的簡寫,只是把函式名稱和teturn拿掉,這樣就比較容易懂了。

例如,原本應該寫成
  '計算身高體重的BMI值
  Function BMI(height As Single,weight As Single)
    Return weight / ((height / 100) ^ 2)
  End Function
就變成(拉成一行)
  Function(height As Single, weight As Single) weight / ((height / 100) ^ 2)

對照著寫就比較容易理解了,函式名稱不見了, 自然End Function也不見了,Return也不見了,差不多這樣,就是Lambda運算式了。
注意:另外一個很重要的是,函式寫在程式碼區塊外面,Lambda運算式則混雜在一般的程式碼裡面。(往下看,就會知道意思)

好,定義出來的Lambda運算式怎麼用呢?
最簡單的方式是在Lambda運算式的外面加上( ), 變成(Lambda運算式)然後再把參數給進去,所以就變成:
(Lambda運算式)(Lambda運算式需要的參數值)
例如:
  Dim ret As Single
  ret = (Function(height As Single, weight As Single) weight / ((height / 100) ^ 2))(170, 60)
  意義是→接收值=(Lambda運算式)(Lambda運算式需要的參數值)

差不多就是這樣,但是,這樣很難用對不對....對!!!

所以,大多數人就寫成這樣:
  Dim BMI = (Function(height As Single, weight As Single) weight / ((height / 100) ^ 2))
  Dim ret As Single
  ret = BMI(170, 60)


其中的BMI我們稱為委派變數,BMI是一個變數,但是在記憶體中是一個函式的位置,所以我們可以呼叫他(傳入170, 60)計算出一個BMI值,再用ret這個Single變數去接收,完成,就是這樣。

不過問題開始浮現,如果要這樣搞,那為何不乾脆一開始就直接建立一個BMI函示就算了?幹嘛用Lambda建立了一個沒有名稱的函示,又要用另一個變數去委派呢???

這還是有原因的,原因也還是跟LINQ有關,我們看一段點底下的程式碼:

  Function test()
    Dim a() As Integer = {1, 2, 3, 4, 5, 6, 7, 8, 9}
    Dim b = From item In a Where item > 3
    Return False
  End Function

其中的變數b的來源值是一段LINQ語法,這段語法會被Compile成:
Public Shared Function test() As Object
  Dim a As Integer() = New Integer() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
  Dim b As IEnumerable(Of Integer) = Enumerable.Where(Of Integer)(a, New Func(Of Integer, Boolean)(Nothing, DirectCast(Module1._Lambda$__1, IntPtr)))
  Return False
End Function

你會注意到,其中就有呼叫到一段Compile幫你自動產生的Lambda運算式。你會發現,Lambda有大半在這個版本的.NET中會出現的原因跟LINQ有著密不可分的關係。所以你會慢慢了解,為何從很多文件和名家的BLOG當中都這麼說,從.NET 2.0之後,其實本質上.NET Framework已經沒有太大的改變,而其他的部分.NET 3.0中的WPF, WWF...等,是額外加上去的,疊在原本的架構之上,同樣的.NET 3.5當中所提供的LINQ機制,也就是NameSpace位於System.Linq底下的,則是在.NET 3.5當中疊在3.0之上的一塊,骨子裡都一樣,所以為了實現LINQ這樣而外加上去的特殊語法,所以增加了Lambda,讓你開發的時候可以很輕鬆的撰寫 From item In a Where item > 3 這樣的語法,再由IDE與Compile通力合作,把要完成的功能隱藏在這段所謂的LINQ語法後面,再透過Lambda和其他的技巧實現,完成現在你看到的偉大工作。

沒有留言: