递归 - 一个语句中的两个调用

时间:2012-10-16 17:10:37

标签: methods recursion call

我试图在下面的代码片段中理解递归调用。

static long fib(int n) {
    return n <= 1 ? n : fib(n-1) + fib(n-2);
}

首先调用哪个函数调用?调用后方程如何工作?

这两个都被调用一次,然后应用方程或第一个被调用,然后是第二个?

也许是一个非常简单的问题!

5 个答案:

答案 0 :(得分:3)

Java和C♯

子表达式按从左到右的顺序进行评估。在fib(n-1)之前评估fib(n-2)。见What are the rules for evaluation order in Java?

重要的是要注意评估的顺序在这里无关紧要,因为fib()没有任何副作用。

C和C ++

这两个函数以不确定的顺序调用,一旦调用它们,它们的返回值将被加在一起并返回。左边的函数可以先调用,或者先调用右边的函数,你不知道。

这似乎有问题,但事实并非如此,因为他们所谓的顺序并不重要。调用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

简而言之,它将完成左表达式的递归,然后移至右。