代码之家  ›  专栏  ›  技术社区  ›  JulianR

调试深层递归的实用技巧?

  •  10
  • JulianR  · 技术社区  · 15 年前

    我正在研究一个棋盘游戏算法,其中使用递归遍历一个大的树,但是,它的行为并不像预期的那样。我该如何处理这些情况?你在这些情况下有什么经历?

    更糟糕的是,它使用了alpha-beta修枝,这意味着树的整个部分永远不会被访问,而且当满足某些条件时,它只会停止递归。我也不能将搜索深度更改为较低的数字,因为虽然它是确定性的,但结果会随搜索深度的不同而变化,并且在较低的搜索深度下,它可能会像预期的那样工作(确实如此)。

    现在,我不会问你“我的代码中的问题在哪里?”但我在寻找一般的技巧、工具、可视化,以及任何调试类似代码的工具。就个人而言,我正在C中开发,但欢迎使用任何和所有工具。尽管我认为这可能最适用于命令式语言。

    8 回复  |  直到 15 年前
        1
  •  22
  •   Paul Sonier    15 年前

    登录中。广泛地登录您的代码。根据我的经验,日志记录是解决这些问题的方法。当很难弄清楚代码在做什么时,广泛地记录它是一个非常好的解决方案,因为它可以让您从代码内部输出内部状态是什么;它实际上不是一个完美的解决方案,但据我所见,它比使用任何其他方法都更好。

        2
  •  6
  •   Dario    15 年前

    也许您可以将递归转换为参数的显式堆栈迭代。通过这种方式测试更容易,因为您可以直接记录值、访问堆栈,并且不必在每个自评估中传递数据/变量,或者防止它们超出范围。

        3
  •  4
  •   dsrekab    15 年前

    我过去做过的一件事是格式化日志以反映递归深度。因此,您可以对每个递归或其他一些分隔符进行新的缩进。然后生成一个调试dll,它记录您需要知道的关于每个迭代的所有信息。在这两者之间,您应该能够读取执行路径,并希望知道哪里出错了。

        4
  •  3
  •   krosenvold    15 年前

    我通常会使用一个或多个具有明确结果的预定义数据集对此类算法进行单元测试。我通常会按复杂性的增加顺序进行一些这样的测试。

    如果您坚持调试,有时使用检查给定值的语句对代码进行检查是很有用的,因此您可以在该时间附加一个断点并将其放在代码中:

      if ( depth = X && item.id = 32) {
         // Breakpoint here
      }
    
        5
  •  2
  •   JPCosta    15 年前

    当我开发一个人工智能算法来玩俄罗斯方块游戏时,我也遇到过类似的问题。在尝试了很多事情之后,我在阅读自己的日志、调试和执行各种函数的过程中浪费了大量的时间,而对我来说,成功的方法是编写一个快速的可视化工具,并使用固定的输入来测试我的代码。

    因此,如果时间不是问题,并且您真的想要了解正在发生的事情,那么就得到一个固定的板状态,并使用调试日志/输出和一些您自己的工具(显示每个步骤的信息),查看您的程序对数据做了什么。

    一旦你找到了一个给你这个问题的电路板状态,试着把功能定位在它开始的地方,然后你就可以修复它了。

        6
  •  1
  •   Ed Altorfer    15 年前

    我知道这会是多么痛苦。在我的工作中,我们目前正在使用一个基本上表现为黑盒的第三方应用程序,因此我们必须设计一些有趣的调试技术来帮助我们解决问题。

    当我在大学上编译器理论课的时候,我们用一个软件库来可视化我们的树;这可能也会帮助你,因为它可以帮助你看到树的样子。实际上,您可以为自己构建一个winforms/wpf应用程序,将树的内容转储到TreeView控件中——这很麻烦,但它可以完成任务。

    您可能还需要考虑某种调试输出。我知道您提到过您的树很大,但是在执行过程中,可能调试语句或在关键点处中断,而您在可视化方面遇到困难,这会对您有所帮助。

    请记住,使用Visual Studio进行智能调试也会产生奇迹。很难看到状态在多个休息时间内是如何变化的,但Visual Studio 2010实际上应该对此有所帮助。

    不幸的是,在没有进一步信息的情况下,帮助您进行调试并不特别容易。你确定它开始破裂的第一个深度了吗?它是否会继续随着搜索深度的增加而中断?您可能希望评估您的工作案例,并尝试确定其不同之处。

        7
  •  0
  •   Brian Rasmussen    15 年前

    既然您说遍历没有按预期工作,我假设您对哪里可能出错有一些了解。然后检查代码以验证您是否忽略了一些基本的东西。

    之后,我建议您设置一些简单的单元测试。如果它们通过了,那么继续添加测试,直到它们失败。如果它们失败了,那么减少测试,直到它们通过或者尽可能简单。这将帮助你找出问题所在。

    如果您还想调试,我建议您使用条件断点。Visual Studio允许您修改断点,因此可以设置何时触发断点的条件。这样可以减少需要查看的迭代次数。

        8
  •  0
  •   dso    15 年前

    我将从检测函数开始。在每次递归调用中,记录数据结构和任何其他有助于识别问题的信息。

    打印出转储文件和源代码,然后离开计算机,在一杯咖啡上进行一次基于纸张的调试会话。