1. 程式人生 > 實用技巧 >初嘗 F# 函數語言程式設計之“用遞迴實現迴圈”

初嘗 F# 函數語言程式設計之“用遞迴實現迴圈”

原教程在這裡 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

初看有點複雜,但它非常直白地表達了這樣一個演算法:

  1. 向函式 addItems 輸入兩個引數:cart 和 items
  2. 如果 items 是空的,就直接原封不動返回 cart
  3. 如果 items 裡面有東西,就取第一個,放進 cart 裡
  4. 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 怎樣寫迴圈?本文初步回答了這個問題。