Recursion: Behind the Scenes [closed]

别来无恙 提交于 2019-12-31 07:40:09

问题


While it is common knowledge that recursion is "a method that calls itself", I am inclined to wonder what is actually happening. Take the classic factorial example:

public static int fact(int n) {
    if(n == 0)
        return 1;
    else
        return n * fact(n - 1);
}

fact(5);

I understand that it goes a little something like this: (the equals sign indicates what's happening when the function is called for that value)

http://postimg.org/image/4yjalakcb/

Why does recursion function like this? Which aspect of a computer makes it work through itself backwards like so? What's going on behind the scenes?

As a student I feel like the recursion we are taught is shallow and general. I want the excellent community here to help me understand it on the level of the machine itself. Thank you!


回答1:


Here's is the brief overview of what happens, whenever you call a method:

  • A frame for that method is allocated from the stack.
  • The frame contains, all the local variables, parameters, return values of the method.
  • That frame is placed over the top of the frame of current method, that invokes this method.
  • When the method returns, the frame related to that method is popped off the stack, and the calling method comes back into action, taking the return value if any from the previous method.

You can learn more about frames here - JVM Spec - Frames.

In case of recursion, same thing happens. Just for the time being, forget that you are dealing with recursion, and take each recursion call as a call to different method. So, in factorial case, the stack would grow like this:

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:


If you trace the function calls, you will see how it works.

E.g.

fact(3) will return 3 * fact(2). So java will call fact(2).

fact(2) will return 2 * fact(1). So java will call fact(1).

fact(1) will return 1 * fact(0). So java will call fact(0).

fact(0) will return 1.

Then fact(1) will return 1 * 1 = 1.

Then fact(2) will return 2 * 1 = 2.

Then fact(3) will return 3 * 2 = 6.


Java calls the recursive method like it would any other method.




回答3:


You might have heard of something called "The Stack" It's what's used to store method states.

I believe it also stores the calling line, so that the function can go back to it's caller

So suppose you have your call to a recursive function

 - int $input = 5
 - stack.Push L
 - GOTO FOO
 - Label L

your recursive function(without a base case) might look similar to the following

 - 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



回答4:


Maybe the following will help you understand. The computer does not care whether he calls the same function it is just computing. There is nothing magical about recursion once you understand what it is and why it works with many things, like lists, natural numbers, etc., who are themselves recursive by structure.

  1. Definition: The factorial of 0 is 1
  2. Definition: The factorial of a number n greater than 0 is the product of that number and the factorial of its predecessor.

Hence

5! = 5*4! = 5*4*3! = 5*4*3*2! = 5*4*3*2*1! = 5*4*3*2*1*0! = 5*4*3*2*1*1 = 120

So, if you ever heard of proof by induction, it goes like this:

  1. We proof some property for a base case.
  2. We proof that, if the property is true for n, then it will be true for the successor of n.
  3. We conclude that this is the proof that the property holds for the base case and all successive cases.

Example: Proof by induction that the square of an even number is a multiple of 4!

  1. The square of 0 is 0, and it is a multiple of 4.
  2. Let n be an even number, whose square n² is a multiple of 4. Then (2+n)*(2+n) = 4+2n+2n+n². This is a muliple of 4, because n² was by our assumption, 4 is one and 2n+2n = 4n is also a multiple of 4 and the sum of multiples of 4 is a multiple of 4 by distributive law: 4a + 4b = 4(a+b)
  3. Q.E.D. The property holds for 0 (our base case), and for (2+n), provided it holds for n. Hence it holds for 2, 4, 6, 8 .... and all other even numbers.

(An easier proof woud be to observe that (2a)² = 4*a*a, which is a multiple of 4.)

Writing a recursive program is very similar to doing a proof by induction:

  1. We write the computation for the base case.
  2. For the non base case, we know how we can compute the result (for example, we know that n! = n * (n-1)!, so we write it just down, since the function we need is the one we are just writing!
  3. We can conclude that our program will compute the correct value for the base case and any successor of the base case. If 678! nevertheless does not compute the correct answer, then it has to do with the fact that we used a data type like int that is not suited very well for big numbers (or, to put it differently, computes everything moulo 2^32) and in addition, with a software that insists on interpreting half of the available numbers as negative ones.

The reason this works has nothing to do with the computer hardware or the programming language: it is, as I said before, a consequence of the recursive structure of the items (lists, trees, sets, natural numbers) at hand.

A common error newcomers make is to ignore the base case and get lost in complexity. I always suggest to start with the base case, once you have this you can assume that the function exists, and can just use it in the more complex cases.



来源:https://stackoverflow.com/questions/18067711/recursion-behind-the-scenes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!