简体   繁体   English

理解递归的麻烦

[英]Trouble Understanding Recursion

Ok, so I have a program: 好的,所以我有一个程序:

public class Rec {
    public static void main(String[] args) {
        test(5);
    }
    static void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);
        System.out.println(n);
        }
    }

It's output is 5,4,3,2,1,1,2,3,4,5. 它的输出是5,4,3,2,1,1,2,3,4,5。 My question is, why/how does the second println(n) statement get executed? 我的问题是,为什么/如何执行第二个println(n)语句? I thought that the function call would completely cut it off, instead it acted in a way that baffles me. 我认为函数调用会完全切断它,而是以令我困惑的方式行事。 This isn't homework or anything, I just really am having a hard time understanding how recursion works. 这不是家庭作业或任何事情,我真的很难理解递归是如何工作的。

All method calls return to the same place once they are done. 一旦完成,所有方法调用都会返回到同一个地方。

By effectively chaining them you get 通过有效地链接他们,你得到

 System.out.println(5)
     System.out.println(4)
        System.out.println(3)
            System.out.println(2)
                system.out.println(1)
                   // If is not true now.
                System.out.println(1)
            System.out.println(2)
        System.out.println(3)
     System.out.println(4)
 System.out.println(5)

Does that make sense? 那有意义吗?

When your test method terminates, it'll resume in the same place it was in the stack frame above. 当您的test方法终止时,它将在上面的堆栈框架中的相同位置恢复。

For example, if I had some code: 例如,如果我有一些代码:

public class Test {
    public static void main(String[] args) {
        System.out.println("A");
        myFunc();
        System.out.println("B");
    }

    public static void myFunc() {
        System.out.println("Do something here");
    }
}

...you'd expect both printlns inside main to run, since myFunc terminates/doesn't enter an endless loop. ...你希望main两个printlns都能运行,因为myFunc终止/不会进入无限循环。

That's true, even if you're using recursion. 这是真的,即使你正在使用递归。 Your test method will eventually terminate, so that means that the second println must be executed at some point in time. 您的test方法最终将终止,这意味着必须在某个时间点执行第二个println。


In contrast, imagine we had a "recursive" function which never terminates: 相反,想象一下我们有一个永远不会终止的“递归”函数:

public class Test {
    public static void main(String[] args) {
        test2(5)
    }

    public static void test2(int n) {
        System.out.println("A " + n);
        test(n - 1);
        System.out.println("B " + n);
    }
}

Because the test2 method never terminates, there's absolutely no way for the second println to execute. 因为test2方法永远不会终止,所以第二个println绝对没有办法执行。 It's for this reason why you should always design any recursive function so that it can terminate when some condition is reached. 这就是为什么你应该总是设计任何递归函数,以便它可以在达到某些条件时终止。

Concept of stack is very important if we were to understand a recursion. 如果我们要理解递归, 堆栈的概念是非常重要的。 Stack is A LIFO data structure. Stack是LIFO数据结构。 Remember whenever a method call happens the current state is pushed into the stack (Current state involves the values of local variables , address of next executable statement etc). 记住每当方法调用发生时,当前状态被推入堆栈(当前状态涉及局部变量的值,下一个可执行语句的地址等)。 Now lets see your problem. 现在让我们看看你的问题。

First this happens, 首先发生这种情况

System.out.println(5);
test(n-1); // method call is happening so store state to stack !!!
//stack contents: n=5 and address of next statement
System.out.println(4);
test(n-1);//another state added to stack : n =4
System.out.println(3);
test(n-1);//another state added to stack: n = 3
System.out.println(2);
test(n-1);//another state added to stack : n = 2
System.out.println(1); 
test(n-1);//another state added to stack : n = 1

Now the condition if(n>0) fails now returning phase of recursion happens ie, the control goes back to the state from which the call was made, and remember all the states were stored in stack and also remember stack is LIFO so now: 现在条件if(n> 0)失败现在返回递归阶段发生,即控制返回到进行调用的状态,并记住所有状态都存储在堆栈中并且还记得堆栈是LIFO所以现在:

// n = 1 first state in stack
System.out.println(1); 
//n = 2 second state stored in stack
System.out.println(2); 
//n = 3 third state stored in stack
System.out.println(3);
//n = 4 fourth state stored in the stack
System.out.println(4); 
//n = 5 last state stored in stack 
System.out.println(5); 

Now all the calls are completed and the control goes back to main. 现在所有的呼叫都已完成,控制权将恢复为主要状态。

Please look your code: 请查看您的代码:

static void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);// this makes the new call to method test() and causes the state to be stored in the stack
        System.out.println(n);// this statement doesn't execute until the recursive call made to test() doesn't return .
        }

HTH :) HTH :)

To assume that your expected result should be "54321", your problem is your second 假设您的预期结果应为“54321”,那么您的问题就是您的第二个问题

System.out.println(n);

The first part of your test-method does exactly what you want it to do: 测试方法的第一部分完全符合您的要求:

if (n > 0) {
    System.out.println(n);
    test(n-1);
// System.out.println(n); 
}

The result would be "54321" - fantastic! 结果将是“54321” - 太棒了! But what happens, because of the second println-method at the end of your test-method, is that you are stacking you output actually (as Crazy Programmer already showed us really precisley). 但是,由于测试方法结束时的第二个println方法,实际上是在堆叠输出(因为Crazy Programmer已经向我们展示了真正的精确度)。 In other words: you don't need the second println-method at all to reach your goal! 换句话说:你根本不需要第二个println方法来实现你的目标! And that's the wonderful thing about recursion: it "breaks" when calling test() again, leaving your second println-method "not executed by now" and starts the whole process again. 这是关于递归的奇妙之处:它再次调用test()时会“中断”,让第二个println方法“暂时不执行”并再次启动整个过程。

What you propably don't see is, that by using 你可以看到的是,通过使用

if (n > 0)

you are verifying that n is greater than 0 (so at least 1), while (at the same time) this is your break condition too! 你正在验证n大于0(所以至少为1),而(同时)这也是你的休息条件! So what your method actually has to do is: 那么你的方法实际上要做的是:

  1. check, if n is greater than 0, 检查,如果n大于0,
  2. print the value of n, and (at last) 打印n的值,并且(最后)
  3. call test-method again (with n-1) 再次呼叫测试方法(用n-1)

If n reaches 0, the whole "method-stacking" dissolves itself (have a look at Niels' solution) and wont return you any further n-values. 如果n达到0,整个“方法堆叠”会自行消解(看看Niels的解决方案)并且不会再返回任何n值。 Got it? 得到它了?

The method is still existing after the recursive call to itself. 在递归调用自身之后,该方法仍然存在。 Once those calls are dealt with, the method will come back to execute the remaining lines of code. 一旦处理完这些调用,该方法将返回执行剩余的代码行。

Have a look at this : http://ideone.com/zWqP8h 看看这个: http//ideone.com/zWqP8h

public static void main(String[] args) {
        test(5);
    }
    static void test(int n) {
        if (n > 0) {
        System.out.println("First step :" + n);
        test(n-1);
        System.out.println("Second step :" + n);
        }
    }

Probably it will help to clarify things. 可能有助于澄清事情。

I think you should trace code then you will get solution. 我认为你应该跟踪代码然后你会得到解决方案。

your code trace something like this. 你的代码跟踪这样的东西。 try to debug this lines this might help you. 尝试调试此行可能会对您有所帮助。

//for n = 5 
void test(int n) {
        if (n > 0) {
        System.out.println(n);
        test(n-1);----->// call of same function
        //for n = 4
        void test(int n) {
            if (n > 0) {
            System.out.println(n);
            test(n-1);-----> 
            //for n = 3
            void test(int n) {
                if (n > 0) {
                System.out.println(n);
                test(n-1);---->
                //for n = 2
                void test(int n) {
                    if (n > 0) {
                    System.out.println(n);
                    test(n-1);---->
                    //for n = 1
                    void test(int n) {
                        if (n > 0) {
                        System.out.println(n);
                        test(n-1); ---->
                        //for n = 0
                        void test(int n) {
                            if (n > 0) {//not satisfy
                                System.out.println(n);
                               test(n-1);
                                System.out.println(n);
                            } 
                            // Till hear you were right but next things you missed.

                        }//again resume of n = 1
                        System.out.println(n);
                        }
                    }//again resume of n = 2
                    System.out.println(n);
                    }
                }//again resume of n = 3
                System.out.println(n);
                }
            }//again resume of n = 4
            System.out.println(n);
            }
        }//again resume of n = 5
        System.out.println(n);
        }
    }//Finish recursion.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM