【问题标题】:Understanding Java recursion using Eclipse's debugger使用 Eclipse 的调试器了解 Java 递归
【发布时间】:2015-04-22 11:36:22
【问题描述】:

我正在通过 Java 中的一些简单递归练习来理解这个概念(我对此感到困惑)。到目前为止,我的所有研究都非常依赖 Eclipse 的调试器来准确理解我的代码在做什么。但是,当涉及到递归时,我发现情况并非如此,因为很难准确跟踪正在发生的事情。

考虑以下代码,返回nth 斐波那契数的方法:

public int fibonacci(int n) {
    if (n == 0 || n == 1) {
        return n;
    } else {
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

在对这段代码使用调试器时,很难准确地跟踪正在发生的事情以及发生的地点/时间。只有一个变量,它每一步都会改变,而且,对于一个小的n 值,例如 7,它已经变得难以跟踪,因为在最终达到 13 之前执行的步骤太多了.

我想知道:

  1. 如何以更好的方式(一般而言)调试我的递归代码,以便更好地理解递归?
  2. 考虑到 return fibonacci(n - 1) + fibonacci(n - 2) 的概念很容易理解,我是否过于关注调试这类事情?

【问题讨论】:

  • 你试过在一张纸上画出调用结构吗?这可能是了解递归在做什么的好练习。
  • 递归方法的输入/输出的简单输出(sysout 或日志)有助于理解恕我直言
  • @RC:请不要通过打印到系统输出来教他调试。我知道有太多的开发人员正是这样做的,这通常会成为生产代码。
  • ++ 不仅要理解递归,还要理解调试器。
  • 您可能需要仔细查看调用堆栈。Eclipse 允许您选择一个堆栈帧并查看该堆栈帧中变量的值。这对于理解递归很有用。

标签: java eclipse debugging recursion


【解决方案1】:

如何调试我的递归代码?

首先,确保您已切换到 Debug 透视图并且您看到的是正确的窗口(@98​​7654322@、ExpressionsDebug 和您的源代码),例如像这样:

接下来,请注意在Debug 中,您可以看到当前调用该方法的频率。该列表会根据调用了多少方法而尚未返回的方法来增长和缩小。

您可以单击其中一种方法来更改范围。查看Variables 的内容在您更改范围时如何变化。

最后,要检查任意内容,请在Expressions 窗口中输入表达式。这几乎就像实时编码。您几乎可以检查任何东西。

我是否过于关注调试?

没有。学会正确地做事,以后会为你节省很多时间。

添加System.out.println() 需要重新编译,并且需要重现并不总是那么简单的情况。

【讨论】:

    【解决方案2】:

    您可以在打印n 值及其fibonnacci 值的每条指令中使用简单的System.out.prinln() 对其进行调试。

    这是一个示例代码:

    public int fibonacci(int n) {
      if (n == 0 || n == 1) {
         System.out.println("your value is: " +n+ " and its Fibonacci value is: "+n);
         return n;
      } else {
        System.out.println("your value is: " +n+ " and its Fibonacci value is: "+fibonacci(n - 1) + fibonacci(n - 2));
        return fibonacci(n - 1) + fibonacci(n - 2);
      }
    }
    

    您可以测试DEMO here

    【讨论】:

      【解决方案3】:

      “内联”代码使 Eclipse 调试器的使用更加困难,因为它非常注重显示不存在的局部变量。您可以通过使事情更详细并保存到变量来使这更容易逐步完成。通过这种方式,您可以更轻松地了解正在发生的事情和结果。例如,如下修改您的代码将使调试器更易于使用:

      public int fibonacci(int n) {
          if (n == 0 || n == 1) {
              return n;
          } else {
              int nMinus1 = fibonacci(n - 1);
              int nMinus2 = fibonacci(n - 2);
              int retValue = nMinus1 + nMinus2;
              return retValue;
          }
      }
      

      免责声明:我没有尝试编译此代码。

      【讨论】:

        【解决方案4】:

        我花了一些时间来掌握递归,但出于某种原因,我从来没有发现调试器很有用。我将尝试向您解释我是如何做的,它不涉及调试器(免责声明:这是一种个人方法,可能不正确或不通用)。

        1. 在递归代码中,您总是至少有一个终止块和一个 递归块。在心理上隔离这两个部分。

          return n; -> 终止块

          return fibonacci(n - 1) + fibonacci(n - 2); -> 递归块

        2. 递归块表示递归的抽象规则。您无需在变量Fn1Fn2 中包含这些值,而是使用相同的函数获取这些值。想想一堵砖墙:您的递归函数创建了墙,将砖添加到现有墙。在递归块内,在某个步骤,您不介意现有墙是由谁以及如何创建的,您只需向其添加新砖块。碰巧墙是由同一个函数创建的,当时是一块砖。

        3. 在终止块中,使用一些值调用代码。在该值的过程结束时应该发生什么?说到斐波那契,在过程结束时(n = 1n = 0)我必须再次将这些数字加到总数中。这是由递归块完成的。换句话说,终止块将具体值(而不是如何获取它们的过程)提供给递归块。

        当我必须进行故障排除时,我会在每一步打印值,这是我为我找到的最佳解决方案。然后我检查它们是否是它们应该是的。对于您的斐波那契,我希望看到类似的输出

        代码:

        public static int fibonacci( int n ) {
            System.out.println( "\nInput value: " + n );
            if( n == 0 || n == 1 ) {
                System.out.println( "Terminating block value: " + n );
                return n;
            }
            else {
                System.out.println( "Recursion block value: fibonacci(" + (n - 1) + ") + fibonacci(" + (n - 2) + ")" );
                int result = fibonacci( n - 1 ) + fibonacci( n - 2 );
                System.out.println( "Recursion block return value: " + result );
                return result;
            }
        }
        

        输出:

        Input value: 4
        Recursion block value: fibonacci(3) + fibonacci(2)
        
        Input value: 3
        Recursion block value: fibonacci(2) + fibonacci(1)
        
        Input value: 2
        Recursion block value: fibonacci(1) + fibonacci(0)
        
        Input value: 1
        Terminating block value: 1
        
        Input value: 0
        Terminating block value: 0
        Recursion block return value: 1
        
        Input value: 1
        Terminating block value: 1
        Recursion block return value: 2
        
        Input value: 2
        Recursion block value: fibonacci(1) + fibonacci(0)
        
        Input value: 1
        Terminating block value: 1
        
        Input value: 0
        Terminating block value: 0
        Recursion block return value: 1
        Recursion block return value: 3
        

        您还可以阅读有关Induction 的有用信息,它与递归严格相关。

        【讨论】:

          猜你喜欢
          • 2016-08-20
          • 1970-01-01
          • 2018-09-11
          • 1970-01-01
          • 1970-01-01
          • 2014-12-30
          • 1970-01-01
          • 2017-03-17
          相关资源
          最近更新 更多