我试图在下面的代码片段中理解递归调用。
static long fib(int n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
首先调用哪个函数调用?调用后方程如何工作?
这两个都被调用一次,然后应用方程或第一个被调用,然后是第二个?
也许是一个非常简单的问题!
答案 0 :(得分:3)
子表达式按从左到右的顺序进行评估。在fib(n-1)
之前评估fib(n-2)
。见What are the rules for evaluation order in Java?
重要的是要注意评估的顺序在这里无关紧要,因为fib()
没有任何副作用。
这两个函数以不确定的顺序调用,一旦调用它们,它们的返回值将被加在一起并返回。左边的函数可以先调用,或者先调用右边的函数,你不知道。
这似乎有问题,但事实并非如此,因为他们所谓的顺序并不重要。调用fib(i)
没有任何副作用(例如修改其他变量,打印消息等),因此两个函数调用完全独立。
一个编译器可能决定评估右侧之前的左侧:
1. f(3)
2. f(2)
3. f(1)
4. return 1
5. f(0)
6. return 0
7. return 1 + 0
8. f(1)
9. return 1
10. return 1 + 1
另一个人可能决定在左前方评估右侧:
1. f(3)
2. f(1)
3. return 1
4. f(2)
5. f(0)
6. return 0
7. f(1)
8. return 1
9. return 1 + 0
10. return 1 + 1
答案 1 :(得分:3)
+
运算符的评估顺序可能未定义(依赖于实现),这意味着:可以先执行fib(n-1)
或fib(n-2)
。无论哪种方式结果都是相同的,在这种特殊情况下它并不重要:两个递归调用都将被计算并在返回之前加在一起,从调用的地方你只能看到总和的最终结果。
答案 2 :(得分:1)
首先调用哪个函数无关紧要。此函数返回Fibonacci序列中的 nth 数字,总是可以通过将前两个数字相加来找到(特殊情况是序列中的前两个数字为0和1)。
因此,这个函数解决fib(n)的问题是要求fib(n-1)和fib *(n-2)并将它们加在一起得到fib(n)。当然,fib(n-1)通过询问fib(n-2)和fib(n-3)起作用,而fib(n-2)通过询问fib(n-3)和fib(n-4)起作用。 )等等,直到达到序列(0和1)的最开始。因为那些可以在没有任何进一步递归的情况下返回,所以递归结束并且每个open函数返回到调用它的那个,一直支持链。
有一种更有效的方法可以做到这一点,不需要两次单独的递归,但它看起来不那么优雅。
答案 3 :(得分:0)
Gcc -S fib.c:
subl $1, %eax
movl %eax, (%esp)
call _fib
movl %eax, %ebx
movl 8(%ebp), %eax
subl $2, %eax
movl %eax, (%esp)
call _fib
所以,左边的那个先叫。然后怎样呢?好吧,它也称为(n-2)没有 知道正确的分支也在计算相同的东西。
这个众所周知的例子具有O(n ^ 2)的复杂度,这意味着如果n = 10,它会使用不同的参数调用它自己~100次,即使10次绰绰有余。
答案 4 :(得分:0)
要理解这一点,我在下面的代码中使用了该代码,并且输出清除了所有疑问:(C#)
static void Main(string[] args)
{
var data = series(5);
Console.WriteLine(data);
}
public static int series(int n)
{
Console.WriteLine(n);
if (n ==2)
{
return 1;
}
if (n == 50)
{
return 3;
}
else
{
return series(2) + series(50);
}
}
输出: 5 2 50 4
简而言之,它将完成左表达式的递归,然后移至右。