Language agnostic 递归如何使运行时内存的使用变得不可预测?

Language agnostic 递归如何使运行时内存的使用变得不可预测?,language-agnostic,memory,memory-management,recursion,factorial,Language Agnostic,Memory,Memory Management,Recursion,Factorial,引用 除了动作缓慢和 运行时内存的使用 不可预测的[2],递归版本 这种例行的工作很难完成 比迭代版本更容易理解, 具体如下: int Factorial( int number ) { int intermediateResult = 1; for ( int factor = 2; factor <= number; factor++ ) { intermediateResult = intermediateResult * factor; } re

引用

除了动作缓慢和 运行时内存的使用 不可预测的[2],递归版本 这种例行的工作很难完成 比迭代版本更容易理解, 具体如下:

int Factorial( int number ) {
   int intermediateResult = 1;
   for ( int factor = 2; factor <= number; factor++ ) {
      intermediateResult = intermediateResult * factor;
   }
   return intermediateResult;
}
int阶乘(整数){
int intermediateResult=1;

对于(int factor=2;factor由于递归方法反复调用它们自己,因此需要大量的堆栈内存。由于堆栈有限,如果超出堆栈内存,则会发生错误。

如果递归级别变得太深,您将炸毁调用堆栈并在过程中消耗大量内存。如果>number
是一个“足够大”的值。你能做得比这更糟吗?是的,如果你的函数在每次递归调用中分配更多的对象

我们不能总是预测需要多少内存吗(我们知道递归应该在什么时候结束)?我认为这和迭代情况一样不可预测,但不再如此

不,一般情况下不会。有关更多背景信息,请参阅关于的讨论。现在,这里是其中一个问题的递归版本:

void u(int x) {
    if (x != 1) {
        u((x % 2 == 0) ? x/2 : 3*x+1);
    }
}

它甚至是尾部递归的。既然你不能预测它是否会正常终止,你怎么能预测需要多少内存呢?

这就是我的想法,但为什么这是不可预测的?我想它只是意味着“取决于运行时的值”。我可以预测的迭代版本总是需要8字节的堆栈(或者其他任何需要的),而我无法“预测”递归值,因为它取决于运行时传入的
number
的值。因此,“不可预测”表示“无法在编译时预测常量值”。如果您不知道函数调用自身的次数,这将是不可预测的,对吗?如果函数调用自身的次数太多,则会出现
堆栈溢出
=)@拉泽:这取决于你的算法。如果你对函数调用自身的次数有限制,那么你可以预测内存使用情况。如果没有,那么它是不可预测的。在阶乘示例中,函数调用自身n-1次来计算n!,因此没有真正的限制。(当然,n!可以表示为
int
的大小没有那么大,如果n更大,我们仍然会得到未定义的行为。)@gideon将增加堆栈保留大小或堆栈提交大小帮助?请注意,如果过程使用尾部递归,并且语言支持尾部调用优化,则递归可以在固定时间内运行。请查阅“尾部递归”如果您感兴趣的话。另外,在一些CPU上,如Sparc,函数调用开销非常低。而且对于一些编程语言,如Tcl,函数调用实际上比内联代码快(这是由于字节编译器的工作方式)。
void u(int x) {
    if (x != 1) {
        u((x % 2 == 0) ? x/2 : 3*x+1);
    }
}