简体   繁体   English

Java:难以理解递归方法调用

[英]Java: trouble understanding recursive method calling

I made an 'Oregon Trail' like game, it uses a 'game over' method to ask if the user wants to play again.我制作了一个类似“俄勒冈小道”的游戏,它使用“游戏结束”的方法来询问用户是否想再玩一次。

The main issue: My teacher mentioned something vague about if the game looped enough times, we'd end up with a stackOverflow.主要问题:我的老师提到了一些关于如果游戏循环次数足够多的话,我们最终会出现 stackOverflow 的情况。 That makes sense to me because the game continues to nest methods inside of each other the way I have it, adding to the stack each time a 'new game' method is called because the outer methods are still there waiting to complete.这对我来说很有意义,因为游戏继续按照我的方式将方法嵌套在彼此内部,每次调用“新游戏”方法时都会添加到堆栈中,因为外部方法仍在那里等待完成。

I've boiled down an example of what I mean.我已经总结了一个例子来说明我的意思。 Assuming there were pauses for user input and such, how am I supposed to make sure my memory utilization doesn't keep growing as I call methods inside other methods?假设用户输入等有暂停,我应该如何确保我的内存利用率不会在我调用其他方法中的方法时继续增长? I think the word for this is 'recursive', thus my title.我认为这个词是“递归的”,因此是我的标题。

If anyone could recommend correct form for dealing with this, I'd be really grateful.如果有人可以推荐正确的形式来处理这个问题,我将不胜感激。

public class Testing
{
    public static void main(String[] args) {
        System.out.println("main method");
        gameStart();
    }
    
    private static void gameStart()
    {
        System.out.println("some other method called");
        gameOver();
    }
    
    private static void gameOver()
    {
        System.out.println("game over called"); //I would ask the user if they want to play again.
        //keeping it concise to illustrate my point, instead of using an if statement
        gameStart();//starting the cycle I'm concerned about. Assume the user indicated they would like to play again.
    }
}

Recursion needs a condition where it will not continue calling.递归需要一个不会继续调用的条件。
Recursion is most commonly seen where a method calls itself , such as computing the fibonacci sequence, where递归最常见于方法调用自身的地方,例如计算斐波那契数列,其中

fib(n) == fib(n-1) + fib(n-2)

fib(0) is defined as 0, so you don't have to compute. fib(0)定义为 0,因此您不必计算。
fib(1) is defined as 1, so you don't have to compute. fib(1)定义为 1,因此您不必计算。
Every other number is computed by the fib() method calling itself twice , but it escapes making the recursive call for the two defined cases, where there is nothing to compute.每隔一个数字由fib()方法计算两次,但它避免了对两个定义的情况进行递归调用,在这种情况下没有什么可计算的。 In pseudo code在伪代码中

int fib(int n)
{
    if (n == 0) return 0; // doesnt have to recursively call
    if (n == 1) return 1; // same
    return fib(n-1) + fib(n-2);
}

In your case, you have two methods that call each other , but you have no condition where the calls can escape from that.在您的情况下,您有两个相互调用的方法,但您没有条件可以从中逃脱调用。

A possibility would be that gameOver() only calls gameStart() when the game ends in a tie, something like一种可能性是gameOver()仅在比赛以平局结束时才调用gameStart() ,例如

public class Testing
{
    public static void main(String[] args) {
        System.out.println("main method");
        gameStart();
    }

    private static void gameStart()
    {
        System.out.println("some other method called");
        gameOver();
    }

    private static void gameOver()
    {
        System.out.println("game over called");
        if (gameTied()) {
            gameStart();
        }
    }
}

If you're just asking "do you want to play again?"如果你只是问“你想再玩一次吗?” -- that would be better done in main , along the lines of - 最好在main完成,沿着

public static void main(String[] args) {
    System.out.println("main method");
    String playGame = "Yes";
    while (playGame.equalsIgnoreCase("Yes") {
        gameStart();
        playGame = ask("Play again?");
    }
}

To avoid unlimited recursion you may switch over to iteration and introduce return values for those methods which currently decide how to proceed (currently by directly calling the corresponding actions).为了避免无限递归,您可以切换到迭代并为当前决定如何进行的那些方法引入返回值(当前通过直接调用相应的操作)。 Let those methods return some sign what to do next, for example by using an enum.让这些方法返回一些下一步要做什么的标志,例如通过使用枚举。 Then write a loop which calls the right methods depending on the return values.然后编写一个循环,根据返回值调用正确的方法。

Example (abbreviated, I assume you know Java syntax):示例(缩写,我假设您知道 Java 语法):

enum Action { Start, ShowEnd, Quit }

main:主要的:

Action nextAction = Action.Start;
while (action != Action.Quit)
{
    switch (action)
    {
        case Start:
            nextAction = gameStart();
            break;
        case ShowEnd:
            nextAction = gameEnd();
            break;
        // ToDo: write more actions!
        default:
            break;
    }
}

This assumes that each such method executes until a decision was made about which action to take next.这假设每个这样的方法都会执行,直到决定下一步要采取什么行动。

This way your call stack will always be quite flat, as the execution always returns to the main method and then branches off into other methods.这样您的调用堆栈将始终非常平坦,因为执行总是返回到 main 方法,然后分支到其他方法。

When you write recursive code you should make sure that you have some sort of end condition BESIDES calling the function again.当您编写递归代码时,您应该确保除了再次调用该函数之外还有某种结束条件。 For example I added an end condition for the gameOver method with if(gamePlayedThisManyTimes <= 1) return;例如,我为gameOver方法添加了一个结束条件if(gamePlayedThisManyTimes <= 1) return; . . When running the following code, the value you give the method gameStart will determine how many games you play and gameOver will decrement the value when it calls 'gameStart' to eventually reach that end condition of the recursion.运行以下代码时,您提供给方法gameStart的值将决定您玩的游戏数量,而gameOver将在调用“gameStart”以最终达到递归的结束条件时递减该值。

public static void main(String[] args)
{
    System.out.println("main method");
    gameStart(10);
}

private static void gameStart(int playGameThisManyTimes)
{
    System.out.println("Game " + playGameThisManyTimes + " started...");
    System.out.println("some other method called");
    gameOver(playGameThisManyTimes);
}

private static void gameOver(int gamePlayedThisManyTimes)
{
    System.out.println("game over called for " + gamePlayedThisManyTimes); //I would ask the user if they want to play again.

    if(gamePlayedThisManyTimes <= 1)
        return;
    else
        gameStart(gamePlayedThisManyTimes - 1);
}

Output输出

main method
Game 10 started...
some other method called
game over called for 10
Game 9 started...
some other method called
game over called for 9
Game 8 started...
some other method called
game over called for 8
Game 7 started...
some other method called
game over called for 7
Game 6 started...
some other method called
game over called for 6
Game 5 started...
some other method called
game over called for 5
Game 4 started...
some other method called
game over called for 4
Game 3 started...
some other method called
game over called for 3
Game 2 started...
some other method called
game over called for 2
Game 1 started...
some other method called
game over called for 1

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

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