相互递归函数中的尾递归

时间:2017-05-30 19:46:11

标签: recursion f# f#-interactive

我有以下代码:

let rec f n =
    if n < 10 then "f" + g (n+1) else "f"
and g n =
    if n < 10 then "g" + f (n+1) else "g"

我想让这些相互递归的函数尾递归以进行优化。 我尝试过以下方法:

let rec fT n = 
    let rec loop a = 
        if n < 10 then "f" + gT (a) else "f"
    loop (n + 1) 
and gT n =
    let rec loop a = 
        if n < 10 then "g" + fT (a) else "g"
    loop (n + 1) 

这是一个正确的尾递归版本吗?如果不是,将非常感谢正确方向的提示。

编辑(第二次采取解决方案):

let rec fA n = 
    let rec loop n a = 
        if n < 10 then loop (n + 1) ("f" + a) else a
    loop n "f"
and gA n =
    let rec loop n a = 
        if n < 10 then loop (n + 1) ("g" + a) else a
    loop n "g"

编辑(第三个解决方案):

let rec fA n a = 
    if n < 10 then gA (n + 1) (a + "f") else a
and gA n a =
    if n < 10 then fA (n + 1) (a + "g") else a

编辑(正确的解决方案):

let rec fA n a = 
    if n < 10 then gA (n + 1) (a + "f") else (a + "f")
and gA n a =
    if n < 10 then fA (n + 1) (a + "g") else (a + "g")

2 个答案:

答案 0 :(得分:6)

你的解决方案肯定是尾递归。

&#34;尾递归&#34;是这样的递归,其中每个递归调用是函数执行的 last 事物。这个概念很重要,因为它意味着运行时可以选择不在调用之间保持堆栈帧:因为递归调用是最后一件事,并且调用函数在此之后不需要做任何其他事情,运行时可以跳过返回对调用函数的控制,并使被调用函数返回到顶级调用者。这允许表达任意深度的递归算法,而不用担心耗尽堆栈空间。

但是,在您的实现中,函数fT.loop调用函数gT,然后预先设置&#34; f&#34;无论gT返回什么。这是&#34; f&#34;在 gT返回后发生,因此对gT的调用 fT.loop的最后一件事。因此,它不是尾递归的。

为了转换&#34;常规&#34;递归到&#34;尾巴&#34;亲切的,你必须&#34;将内部的逻辑转向&#34;,可以这么说。让我们看看函数f:它调用g然后预先设置&#34; f&#34;无论g返回什么。这&#34; f&#34;前缀是整个&#34;贡献&#34;在总计算中的函数f。现在,如果我们想要尾递归,那就意味着我们无法做出贡献&#34;在递归调用之后。这意味着贡献必须在之前发生。但如果我们在电话会议之前做出贡献并且之后不做任何事情,那么我们如何避免失去这种贡献呢?唯一的方法是将贡献作为参数传递给递归调用

这是尾递归计算背后的一般思路:我们不是等待嵌套调用完成然后向输出中添加内容,而是先进行添加并传递到目前为止已经添加的内容&#34; ;进入递归调用。

回到你的具体例子:因为f的贡献是&#34; f&#34;字符,它需要将此字符添加到已计算的字符#34;到目前为止&#34;并将其传递给递归调用,然后执行相同的操作,依此类推。到目前为止&#34;&#34;参数应该具有&#34;计算你要计算的任何东西的语义,然后在我的目前为止预先设置&#39;那个&#34;。

由于您只是要求提示&#34;这显然是家庭作业(请原谅我,如果我错了),我不会发布实际的代码。如果你相信我做了,请告诉我。

答案 1 :(得分:2)

我认为你的尝试肯定不会将递归置于尾部位置

如何处理将递归移动到尾部位置将使用延续。为此,我们必须实施具有延续参数的google charts tablefk变体,然后gkf可以使用{{1}实现分别是g

我不是F#专家,但我可以用JavaScript简单地说明这一点。我通常不会使用不同的语言发布答案,但由于语法非常相似,我认为这对您有所帮助。它还有一个额外的好处,你可以在浏览器中运行这个答案,看它是否正常工作

&#13;
&#13;
fk
&#13;
&#13;
&#13;