@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实现的。
所以,你的函数有一个尾递归版本,但只是因为我们可以简单地将过去的三个值保存在一个元组中。一般来说,如果计算出您需要的先前值的代码做了更复杂的事情,则这可能不可行。