Java 递归:幕后
众所周知,递归是“一种调用自身的方法”,但我倾向于想知道实际发生了什么。以经典的阶乘为例:Java 递归:幕后,java,recursion,Java,Recursion,众所周知,递归是“一种调用自身的方法”,但我倾向于想知道实际发生了什么。以经典的阶乘为例: public static int fact(int n) { if(n == 0) return 1; else return n * fact(n - 1); } 事实(5) 我知道它是这样的:(等号表示为该值调用函数时发生的情况) 为什么递归的功能是这样的?计算机的哪一方面使它能像这样通过自身反向工作?幕后发生了什么 作为一名学生,我觉得我们所学的
public static int fact(int n) {
if(n == 0)
return 1;
else
return n * fact(n - 1);
}
事实(5)
我知道它是这样的:(等号表示为该值调用函数时发生的情况)
为什么递归的功能是这样的?计算机的哪一方面使它能像这样通过自身反向工作?幕后发生了什么
作为一名学生,我觉得我们所学的递归是肤浅而笼统的。我希望这里的优秀社区能够帮助我从机器本身的角度来理解它。谢谢大家! 如果跟踪函数调用,您将看到它是如何工作的 例如
fact(3)
将返回3*fact(2)
。因此java将调用fact(2)
fact(2)
将返回2*fact(1)
。因此java将调用fact(1)
事实(1)
将返回1*事实(0)
。因此java将调用fact(0)
事实(0)
将返回1
然后fact(1)
将返回1*1=1
然后fact(2)
将返回2*1=2
然后fact(3)
将返回3*2=6
Java像调用任何其他方法一样调用递归方法 以下是调用方法时发生的情况的简要概述:
- 从堆栈中为该方法分配一个帧
- 框架包含方法的所有局部变量、参数和返回值
- 该框架放置在调用此方法的当前方法的框架顶部
- 当该方法返回时,与该方法相关的帧从堆栈中弹出,并且调用方法返回到操作中,如果有返回值,则从上一个方法获取返回值
factorial
情况下,堆栈的增长如下:
fact(5)
5 * fact(4)
4 * fact(3)
3 * fact(2)
2 * fact(1)
1 * fact(0) // Base case reached. Stack starts unwinding.
2 * 1 * 1
3 * 2 * 1 * 1
4 * 3 * 2 * 1 * 1
5 * 4 * 3 * 2 * 1 * 1 == Final result
您可能听说过一种叫做“堆栈”的东西,它是用来存储方法状态的 我相信它还存储了调用线路,这样函数就可以返回到它的调用者 因此,假设您调用了一个递归函数
- int $input = 5
- stack.Push L
- GOTO FOO
- Label L
您的递归函数(没有基本情况)可能类似于以下内容
- Label FOO
- int in = $input
- input = in - 1
- stack.Push in
- stack.Push L2
- goto FOO
- Label L2
- in = stack.Pop in
- output *= in
- goto stack.POP
也许下面的内容可以帮助你理解。计算机不在乎他是否调用了相同的函数,它只是在计算。一旦你了解了递归是什么,以及它为什么能处理很多事情,比如列表、自然数等,它们本身就是结构递归的,那么递归就没有什么神奇之处了
fact(5)
5 * fact(4)
4 * fact(3)
3 * fact(2)
2 * fact(1)
1 * fact(0) // Base case reached. Stack starts unwinding.
2 * 1 * 1
3 * 2 * 1 * 1
4 * 3 * 2 * 1 * 1
5 * 4 * 3 * 2 * 1 * 1 == Final result
(2+n)*(2+n)
=4+2n+2n+n²
。这是4的倍数,因为根据我们的假设,n²是1,2n+2n=4n
也是4的倍数,根据分布定律,4的倍数之和是4的倍数:4a+4b=4(a+b)
(2a)²
=4*a*a
,这是4的倍数。)
编写递归程序非常类似于通过归纳进行证明:
n!=n*(n-1)!
,因此我们只写下它,因为我们需要的函数就是我们刚刚编写的函数678!
仍然无法计算正确的答案,那么这与我们使用的数据类型如int
不太适合大数字有关(或者,换言之,计算moulo 2^32的所有数据)此外,软件坚持将可用数据的一半解释为负数新手常犯的一个错误是忽略基本情况而迷失在复杂性中。我总是建议从基本情况开始,一旦你有了基本情况,你就可以假设函数存在,并且可以在更复杂的情况下使用它。“递归为什么会这样?”因为这就是你告诉它要做的?这是由代码规定的简单、循序渐进的方法?调用函数,等待其结果。冲洗,重复。看看是什么使递归以这种方式计算?没有“计算机的方面”这使得它的工作原理是这样的——递归代码和其他代码一样,机器处理r