我知道递归函数的基本定义.....但我想知道它对内存的影响? 为什么不选择循环或迭代?
答案 0 :(得分:6)
通常不会出于以下两个原因之一选择它:
虽然标准对堆栈空间没什么好说的,但它往往是一个容易出现溢出的有限资源。
例如,这是一个糟糕的递归函数:
def addTwoPositiveNumbers (a,b):
if b == 0:
return a
return addTwoPositiveNumbers (a+1,b-1)
(除非它可以做尾部优化)。那是因为它理论上可以使用堆栈级别的 lot ,例如当b = 1,000,000,000
时,它将使用十亿个堆栈帧。
另一方面,二进制搜索也不算太糟糕,因为它使用每个级别的递归删除了一半的搜索空间。因此,即使您有十亿个条目,也只能记录 2 1,000,000,000或30个堆栈级别。
列出了我认为人们避免它的原因,我应该说我使用它很多,你应该意识到许多问题自然表达为一种递归解决方案。代码更清晰,更易读。仅仅因为这个原因,它应该是你的武器库的一部分。
答案 1 :(得分:2)
C ++不要求尾部调用优化,因此可以简单地转换为循环的递归函数仍然可以在调用深度(存储帧)中占用线性空间。
此外,C / C ++不会必然检测到堆栈溢出,因此这可能是深度调用的另一个问题。
已编辑以获得更多合格语言(“必然”)
修改强>
有些人似乎对gcc和clang等特定编译器执行尾调用优化和/或堆栈溢出检测这一事实感到困惑。关键是,如果没有强制要求,那就不安全了。例如,gcc仅在-O2,-O3和-Os执行尾调用优化。因此,如果程序依赖执行尾调用优化,当有人编译时没有优化标志(在gcc上),程序将会神秘地死掉。
如果它是良好的编程习惯,那么,无论如何,它们都可以继续编写依赖于编译器标志的程序。或者那些依赖编译器的人。优化不是可选的。
答案 2 :(得分:2)
您始终可以使用迭代操作重写递归 - 问题变为可读性/易于实现。对于相当多的问题,递归导致更简单的算法(例如,与图/树相关的大多数东西)。
就内存而言:在C或C ++这样的语言中,每个函数调用都需要将其参数推送到调用堆栈。这个堆栈的大小有限(100个长的调用链几乎总是好的 - 1000000几乎肯定不行)。确切的限制是高度依赖系统的。
更多高级语言,特别是功能性语言,有“技巧”来启用堆栈,在某些情况下看似“无限”(尾部调用,这是非常常见的情况)。这些语言通常保证优化,称为尾部调用优化(TCO),它可以在递归有意义的情况下实现优雅的代码。
答案 3 :(得分:0)
除非编译器能够优化递归调用(尾递归优化),否则每个invokation都需要所有参数和局部变量的复制以及返回地址。那东西必须存放在通常在堆栈上的某个地方。因此影响是一次调用的内存量乘以后续调用的次数。
此外,当invokation深入到递归时,堆栈的新形式被占用,这可能导致缓存未命中,这可能会降低执行速度,也可能归因于内存影响。
答案 4 :(得分:0)
考虑递归实际上是如何工作的,并且对内存的影响是明确的。首先要了解一般的函数调用是如何工作的。您可能想要启动here。
答案 5 :(得分:0)
通常可以避免使用递归函数,因为
如果函数不可重入,它们也可能会产生不良后果。
答案 6 :(得分:0)
由于堆栈溢出问题,通常会避免递归。迭代解决方案没有这个问题,所有递归问题都可以重写为使用迭代(有些比其他更容易)。此外,许多程序员根本不熟悉递归(它经常不会出现这种情况) - 稍后维护代码的人可能无法理解正在发生的事情并进行修改,从而搞砸了一些东西。这通常是一件坏事。其他语言(特别是功能语言)的递归通常更常见,因为它们处理它的方式。