Ocaml 如何将此函数转换为尾部递归

Ocaml 如何将此函数转换为尾部递归,ocaml,tail-recursion,Ocaml,Tail Recursion,我不知道怎么做,因为那里有一个递归函数的总和 感谢@Jeffrey Scofield的代码尾部递归并不是您这里最大的问题。将递归函数转换为尾部递归函数通常有助于将线性递归或近似线性递归的内存使用从堆栈移动到堆,甚至有希望减少该过程中的内存使用。之所以这样做是因为后者不太可能溢出 在您的情况下,我看不出堆栈溢出是一个很大的问题,因为更早的时候,对于更小的n-k,您将运行到极端的运行时间。原因是递归远远不是线性的:它通过k分支 您将要应用的技术是动态编程。作为一个旁注,根据实现情况,它还可以为您提供

我不知道怎么做,因为那里有一个递归函数的总和


感谢@Jeffrey Scofield的代码

尾部递归并不是您这里最大的问题。将递归函数转换为尾部递归函数通常有助于将线性递归或近似线性递归的内存使用从堆栈移动到堆,甚至有希望减少该过程中的内存使用。之所以这样做是因为后者不太可能溢出

在您的情况下,我看不出堆栈溢出是一个很大的问题,因为更早的时候,对于更小的n-k,您将运行到极端的运行时间。原因是递归远远不是线性的:它通过k分支

您将要应用的技术是动态编程。作为一个旁注,根据实现情况,它还可以为您提供恒定的堆栈使用率

[编辑] 你在评论中指出你的问题是学术性的。如果您坚持尾部递归,有几个选项

函数调用只能有一个尾部。因此,严格来说,尾部递归只能应用于最后一个递归调用,而不是所有的递归调用。在这种情况下,使用累加器很容易实现这一点:使用非尾部递归计算除一项之外的所有项的和,并使用尾部递归计算最后一项,通过累加器传递部分和

通过递归函数的执行,我们可以关联调用树。在您的例子中,树的高度为n-k,每个非叶子都有k个子树。然后,控制流模拟通过该树的深度优先行走。可以在单个循环中模拟此深度优先行走。树中需要跟踪的位置与调用堆栈密切相关。然后,可以使用尾部递归实现这个循环


您的代码不是OCaml,甚至不完全可理解。但我猜你在寻找这个函数:

let rec hf0 k n =
if n <= k then n
else
    let rec loop i =
        if i > k then 0 else hf0 k (n - i) + loop (i + 1)
    in
    loop 1
下面是一个尾部递归版本:

let rec hf0 k n =
    if n <= k then n
    else
        let rec loop i =
            if i > k then 0 else hf0 k (n - i) + loop (i + 1)
        in
        loop 1
为简单起见,此代码假定k>=0

正如@kne所说,它通过将中间计算从堆栈移动到堆来工作。特别是,它跟踪数组prev中以前的值

更新

这个函数很容易转换,因为它是类似斐波那契计算的简单推广


但一般来说,您可以使用将任何计算转换为尾部递归。

我认为您是对的。但实际上这只是一个学习示例,我正在学习如何将普通递归转换为尾部递归。
let hf k n =
    let prev = Array.make k 0 in
    let rec ihf m =
        let res =
            if m <= k then m else Array.fold_left (+) 0 prev
        in
        if m >= n then
            res
        else
            begin
            Array.blit prev 0 prev 1 (k - 1);
            prev.(0) <- res;
            ihf (m + 1)
            end
    in
    if n <= k then n
    else if k <= 1 then k
    else ihf 0