当前位置: 首页 > 知识库问答 >
问题:

嵌套递归调用-这是尾部递归吗?

隆扬
2023-03-14

我想我理解了教科书中对尾部递归函数的定义:在函数调用后不执行任何计算的函数。我还发现,作为一个结果,尾部递归函数的内存效率会更高,因为它每次调用只需要一条记录,而不是每次都需要保留一条记录(就像在普通递归中那样)。

我不太清楚的是,这个定义如何应用于嵌套调用。我将提供一个例子:

func foo91(x int)
    if(x > 100):
        return x - 10
    else:
        return foo91(foo91(x+11))

我最初给出的答案是,根据定义,它不是尾部递归的(因为外部调用是在计算内部调用之后执行的,所以其他计算是在第一次调用之后完成的),所以通常具有嵌套递归调用的函数不是尾部递归的;另一方面,在实践中也是一样的,因为它共享尾部递归函数的副作用:在我看来,整个函数需要一个激活记录。这是真的吗?

嵌套递归函数调用通常是相当大的尾部递归吗?

共有1个答案

呼延凌
2023-03-14

你的理解只是部分正确。

您正确定义了尾部递归。但它是否真正有效取决于实现。在某些语言中,如Scheme,它就是。但大多数语言都像JavaScript,事实并非如此。特别是一个关键的权衡是,使尾部递归有效意味着丢失堆栈回溯。

现在回到你的实际问题,内部调用不是尾递归的,因为它必须回到你的调用并做其他事情。但是外部调用是尾递归的。

 类似资料:
  • 我对函数式编程很陌生,尤其是下面使用的Scheme。我正在尝试使以下函数是递归的,尾递归的。基本上,该函数的作用是对两个字符串的对齐方式进行评分。当给定两个字符串作为输入时,它会比较每个“列”字符,并根据在称为 scorer 的函数中实现的评分方案(由下面的代码中的函数调用)来累积该对齐的分数。 我有一个想法,用一个帮助函数来累积分数,但我不太确定如何去做,因此我该如何让下面的函数尾递归呢?

  • 我只是想知道这样的函数是否可以递归地完成。我觉得很难,因为它自己叫了两次。 这是我在javascript中的非尾部递归实现。(是的,我知道大多数javascript引擎不支持TCO,但这只是理论上的。)目标是找到给定数组(arr)中具有特定长度(大小)的所有子列表。例如:getSublistsWithFixedSize([1,2,3],2)返回[[1,2]、[1,3]、[2,3]]

  • 我有一个(co?)递归函数对,它们处理元组列表,并根据一些开始和结束条件将它们折叠成批处理。 我做得不多,所以我可能很愚蠢。 我已经修改了一个简单的非尾部递归版本,通过明确引入一个“tot”参数来构成当前折叠状态,我认为这是尾部递归的,但我在大输入上得到了可怕的堆栈溢出。。。。(在调试器和(调试)中)。exe) 作为一个明确的折叠,可能有更好的方法来做到这一点...但这几乎不是重点,重点是为什么它

  • 我试图了解如何将各种递归函数转换为尾递归。我已经查看了许多将斐波那契和阶乘转换为尾递归的示例,并理解了这些示例,但很难跳到具有某种不同结构的问题。一个例子是: 如何将其转换为尾部递归实现? 我已经看过类似的问题,例如:将正常递归转换为尾部递归,但这些似乎并没有转化为这个问题。

  • 我有一个关于如何将“递归”转换为“尾部递归”的问题。 这不是家庭作业,只是在我试图润色算法书籍中的递归定理时出现的一个问题。 我熟悉使用递归的两个典型示例(阶乘和斐波那契序列),也知道如何以递归方式和尾部递归方式实现它们。 我的代码如下(我使用Perl只是为了使其简单,但可以轻松地转换为C/Java/C)。 运行代码时,输出如下: 递归函数在返回之前使用不同的参数调用自己两次。我尝试了几种方法将其

  • 尾递归优化 recur 尾递归优化是函数式编程不能缺少的一个性能优化方案. 没有尾递归, 常有的递归调用也会形成很深的调用栈消耗内存. cljs 和 Clojure 类似, 都需要通过声明 recur 进行优化. 最终代码会被编译为 white 结构的 js 代码,从而提高性能. (defn factorial [acc n] (if (<= n 1) acc (recur (* ac