初嘗 F# 函數語言程式設計之“用遞迴實現迴圈”
阿新 • • 發佈:2020-12-01
原教程在這裡 https://fsharpforfunandprofit.com/posts/designing-for-correctness/
但下面的內容是我在原教程的基礎上自己新增的,原教程裡沒有下面的內容。
原教程裡定義了一個型別 Cart, 但下面我們不用關心 Cart 的具體定義,只需要知道它表示一個購物車,下面我用一個列表 ["aaa"; "bbb"; "ccc"]
表示貨物。
然後我想做的是,像普通程式設計方法的 while 或 for 迴圈那樣,迴圈新增貨物進購物車裡。
在函數語言程式設計中,通常用遞迴來實現迴圈,如下所示:
let rec addItems (cart: Cart) items = match items with | [] -> cart | head :: tail -> let cartFilled = cart.Add head addItems cartFilled tail let cart = Cart.New let items = ["aaa"; "bbb"; "ccc"] let cartFilled = addItems cart items
初看有點複雜,但它非常直白地表達了這樣一個演算法:
- 向函式 addItems 輸入兩個引數:cart 和 items
- 如果 items 是空的,就直接原封不動返回 cart
- 如果 items 裡面有東西,就取第一個,放進 cart 裡
- items 除去第一個之後剩下的東西按上述 1~3 的步驟重複操作
另外,上面程式碼的最後兩行,可以用 F# 的特性改寫成這樣:
let cartFilled = ["aaa"; "bbb"; "ccc"] |> addItems cart
同樣的功能,也可以用另一種方法來實現:
let addItems (cart: Cart) items = let action (cartFilled: Cart) item = cartFilled.Add item match items with | [] -> cart | head :: tail -> List.fold action (cart.Add head) tail
如果不需要考慮 items 為空的情況,甚至可以進一步簡化如下:
(但要注意,以下程式碼當 items 為空時會出錯)
let action (cartFilled: Cart) item = cartFilled.Add item
let cartFilled = List.fold action (cart.Add items.Head) items.Tail
另外,根據具體情況,有時 List.fold 可以非常簡單好用(甚至可以處理列表為空的情況),或者採用 List.filter, List.map, List.scan, List.iter, List.reduce 等,都是很常用也很好用的工具。
本文介紹了在 F# 裡實現迴圈的幾種方法,雖然沒有體現 F# 或函數語言程式設計的優勢,但初學函式式通常都會有這樣的疑問:不用 while 不用 for 怎樣寫迴圈?本文初步回答了這個問題。