2011年8月12日 星期五

F# Language Concepts - Functions

這一部分的東西是直接從Microsoft MSDN上面取得的, 就想成是中文化後的就行了, 至於真正MSDN上面有沒有中文化我就不得而知了, 個人的習慣是比較喜歡直接看英文, 反正字也不多。下面並沒有直的直接翻譯所有的文字, 全都是領悟之後所寫下來的, 如果有什麼地方不好, 也請和我說明, 我會做修改。
第一部分介紹的是function, 看一下下面的定義…
// 非recursive function定義
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function 定義
let rec function-name parameter-list = recursive-function-body
看得出有什麼不一樣嗎?其實很簡單, 在沒有rec的關鍵字下, 就是一般的function定義, 另外F#裡用的是let binding來決定一個function name, 而且function也沒有() <-來傳入參數, 參數是直接定義在後面的。還有一點很重要, 大概所有的F# function都不用定義回傳值的type, 及傳入參數的type, 這是因為F#會自動推導所有參數及回傳值的型別, 但如果是要指定也是可以的, 只是會有些麻煩, 在下面的例子中會一一看到。
let f x = x + 1
裡定義了f是一個function, 傳入參數為x, 回傳值就是x+1, 如果想呼叫這個function, 就可以像這樣
let myX = f 2
如此就會出現myX的值為3
另外如果想直接指定型別的話, 也可以這樣寫
let f (x: int) = x + 1
這樣一來x就必需是int, 而f的傳出值也是int
再來我們看一下最神奇的, 部分參數傳入的function
let cylinderVolume(radius, length) =
    let pi = 3.14159
    length * pi * radius * radius
let smallPipeRadius = 2.0
let bigPipeRadius = 3.0

// These define functions that take the length as a remaining
// argument:

let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
這一部分大概是F#語言神奇的地方, 不過依照式子的推導其實也不是那麼不可理解, 依let bindding cylinderVolume是一個function, 其參數是一數對(radious, length), 在smallPipeVolume時我們只有推導式子到第一個參數smallPipeRadius, 直到let smallPipeVol1時才把length1傳入, 因此我們在第二次推導時才得到結果。

再來我們來看看recursive的function是如何定義的, 在MSDN上面是寫, 會呼叫自己的function稱為recursive, 好像有點道理, 其實在語言的實作上, 只要加入stack就可以了, 但我們還是不要搞得太複雜, 直接看一個有名的例子。費式:
let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n – 2)
這樣一來就行了, recursive的重點在於終止條件, 如果沒有終止條件, 就會讓你的程式掛點, 請小心…
再來升紹一個東西叫做function value, 顧名思意就是, 把function當成value傳到另一個function裡, 這個方法其實在現在的語言裡都不是什麼難題了, 只是在FP界裡, 因為function也是一種value, 所以可以傳進另一個function裡。
讓我們來看一個例子
let apply1 (transform : int -> int ) y = transform y
上面說明了apply1的參數有兩個, 第一個是一個function, 這個function的傳入參數是一個int, 傳出也是int, apply1第二個參是一個y, 而apply1這個function的回傳值則是第一個function (參數), 傳入y而得的傳出值。用中文解釋真的很麻煩…
讓我們看一下怎麼使用…
let increment x = x + 1

let result1 = apply1 increment 100
當然result1就是101囉..
第二個例子
let apply2 ( f: int -> int -> int) x y = f x y

let mul x y = x * y

let result2 = apply2 mul 10 20
這樣result2是多少呢? 200, 依照式子反推回去就可以得到算式結果囉..
接下來來看Lambda expression, 可以想成是不具名的function, 看例子比較容易理解
let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y ) 10 20

最後介紹function composition, 這可以算是最神奇的東西, 但依數學式子的推導, 也不是那麼難以理解的。
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
答案呢?猜一下吧…
說明一下在數學上的定義,
function1(x) = x + 1;
function2(x) = x * 2;
於是把function1代入function2裡 => function2(function1(x)) = function2(x+1) = (x+1) * 2, 至此, 應該就解出答案是202了…

沒有留言: