@bytebuster的解決方案很好,但他沒有解釋他是如何創建它的,所以它只會幫助解決這個特定的問題。順便說一句,你的公式看起來有點像斐波納契(但不完全),它可以是calculated analytically without any looping(即使沒有循環隱藏在Seq.unfold)。
你開始用下面的函數:
let rec f0 n =
match n with
| 0 | 1 | 2 -> 1
| _ -> f0 (n - 2) + f0 (n - 3)
的函數調用f0的參數n - 2和n - 3,所以我們需要知道這些值。訣竅是使用dynamic programming(可以使用memoization完成),但由于您不想使用memoization,所以我們可以手動編寫它。
我們可以寫f1 n,它返回一個三元組元組,其當前值和兩個過去值為f0。這意味著f1 n = (f0 (n - 2), f0 (n - 1), f0 n):
let rec f1 n =
match n with
| 0 -> (0, 0, 1)
| 1 -> (0, 1, 1)
| 2 -> (1, 1, 1)
| _ ->
// Here we call `f1 (n - 1)` so we get values
// f0 (n - 3), f0 (n - 2), f0 (n - 1)
let fm3, fm2, fm1 = (f1 (n - 1))
(fm2, fm1, fm2 + fm3)
此功能無法尾recurisve,但它只是遞歸調用自己一次,這意味著我們可以使用蓄能器參數的模式:
let f2 n =
let rec loop (fm3, fm2, fm1) n =
match n with
| 2 -> (fm3, fm2, fm1)
| _ -> loop (fm2, fm1, fm2 + fm3) (n - 1)
match n with
| 0 -> (0, 0, 1)
| 1 -> (0, 1, 1)
| n -> loop (1, 1, 1) n
我們需要處理參數0和1專門在fc的主體中。對于任何其他輸入,我們從最初的三個值(即(f0 0, f0 1, f0 2) = (1, 1, 1))開始,然后循環n次執行給定的遞歸步驟,直到達到2為止。遞歸loop函數是@bybbuster的解決方案使用Seq.unfold實現的。
所以,你的函數有一個尾遞歸版本,但只是因為我們可以簡單地將過去的三個值保存在一個元組中。一般來說,如果計算出您需要的先前值的代碼做了更復雜的事情,則這可能不可行。