【问题标题】:Return function returning to main instead of caller返回函数返回主而不是调用者
【发布时间】:2013-09-17 12:30:18
【问题描述】:

只是想知道...

void mazeTraversal(char maze[12][12], int x, int y)
{
if (maze[x + 1][y] == '.')
    mazeTraversal(maze, ++x, y);
if (maze[x][y + 1] == '.')
    mazeTraversal(maze, x, ++y);
if (maze[x - 1][y] == '.')
    mazeTraversal(maze, --x, y);
if (maze[x][y - 1] == '.')
    mazeTraversal(maze, x, --y);
   return;
}

当我遇到死胡同时,我应该恢复到调用的前一个实例,而不是直接返回主函数吗?如果我有什么问题,请告诉我。

【问题讨论】:

  • “我应该恢复到之前的通话实例”你是什么意思?
  • 这是什么语言?
  • @user2646981 你使用什么编译标志?可能是你被尾递归优化所困扰吗? (另外,如果您使用 C,请将其标记为 C)
  • @DennisMeng 他们在 gcc 中是否默认开启?如果不是,那我怀疑。我只有调试和汇编输出选项..
  • 那么,您在程序集中看到了什么?如果它正在做一些意想不到的优化,那将是找出答案的一种方法

标签: arrays recursion parameters return main


【解决方案1】:

以下地址使用返回值来捕获“正确路径”信息以及提供base case condition。然而,原始代码的另一个关键问题是使用++/--,这会对各个变量造成副作用。出于这个原因,下面的代码避开了这些运算符。


执行此操作的主要方法是返回一个值 - 例如,如果此路径导致并结束,则为 true,否则为 false

一个非常原始的转换将如下所示。注意基本情况的添加——这意味着我们可以停止递归。返回 True 以便之前的调用者也知道他们可以停止查找,并且函数堆栈将快速展开回“main”。

bool mazeTraversal(char maze[12][12], int x, int y) {
  // This is the BASE CASE and means WE ARE DONE LOOKING
  if (maze[x][y] == 'X') return true; // found end!

  if (maze[x + 1][y] == '.')
    if (mazeTraversal(maze, x + 1, y)) return true;

  if (maze[x][y + 1] == '.')
     if (mazeTraversal(maze, x, y + 1)) return true;

  if (maze[x - 1][y] == '.')
     if (mazeTraversal(maze, x - 1, y)) return true;

  if (maze[x][y - 1] == '.')
     if (mazeTraversal(maze, x, y - 1)) return true;

  return false; // didn't find any valid path from here
}

请注意,我删除了 ++/-- 以避免奇怪的副作用

但是,我们可以做得更好,并清理一些代码。关键的区别在于每个递归调用都会检查 当前位置 - 而不是其邻居的位置。

bool mazeTraversal(char maze[12][12], int x, int y) {
  if (maze[x][y] == 'X')
    return true; // found end!

  if (maze[x][y] == '.') {
    // we are still on a path - see where exploration leads!
    if (mazeTraversal(maze, x + 1, y)) return true;
    if (mazeTraversal(maze, x, y + 1)) return true;
    if (mazeTraversal(maze, x - 1, y)) return true;
    if (mazeTraversal(maze, x, y - 1)) return true;
  }

  // Didn't find any valid path from here -
  // maybe "here" is a wall!
  return false;
}

最终,您可能希望在找到的路径上执行其他操作(即在“返回真”的位置),例如记录当前位置。

还要注意,根据语言,if (mazeTraversal..) return true 的内容也可以“清理”。在 C 中,考虑使用 short-circuiting || 运算符。然后我们可以将最终情况缩短为:

int leadsToEnd =
       (maze[x][y] == 'X')      // at end
    || (maze[x][y] == '.')      // or not end ..
       && (mazeTraversal(maze, x + 1, y)   // but may lead to end
           || mazeTraversal(maze, x, y + 1)
           || mazeTraversal(maze, x - 1, y) 
           || mazeTraversal(maze, x, y - 1));

因此整个函数实际上变成了return leadsToEnd(根据需要替换上面的表达式)。当然,您必须决定哪种方法 - 后一个示例可以说过于聪明了 - 或者这种方法的混合对您来说最有意义。

【讨论】:

  • 这是最复杂的方法,但它有效。太棒了……非常感谢。但是,我仍然对为什么我的方法不起作用感到困惑......我知道 void 类型很好,而不是实际值,但我认为 void 函数仍会像普通函数一样返回......
  • 谢谢...不过,仅用于教育目的...您能解释一下为什么我最初的方法不起作用吗?
  • 我确实复制了您的方法(用 int 代替 bool),效果非常好。但是,仍然有问题:1)我没有包含基本情况,我理解,但是当说返回时,它不应该恢复到调用者函数吗?假设我使用了第一个条件语句,调用了该函数,然后发现它是一个前端。当我到达被调用函数中的 return 语句时,它不应该返回调用者,而不是返回 main 吗? 2)增量器和减量器会导致哪些副作用?
  • 我已经更新了一些答案。另请参阅维基百科递归文章的链接,并查看 基本案例
  • 哇..我刚刚意识到。增量器永久更改变量。我可能刚刚在那里添加了 x = x + 1 。这么小的错误......至少现在我的原始代码有效(当它到达迷宫的出口时,我添加了一个基本案例)。谢谢你提到那个错误
【解决方案2】:

正如你现在所拥有的,当你遇到死胡同时,你就回来了。这是正确的,因为您将备份到上一个呼叫,然后选择下一个路径。当您遍历所有可能的路径后,您的顶级调用将返回。

【讨论】:

  • 好吧,你看,它实际上又回到了主线:这让我很困惑。我还假设它会像逻辑上假设递归那样堆叠起来,但是......它几乎就像返回是一个 goto main...... P.S.:C 标准
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-10
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
  • 2016-07-10
相关资源
最近更新 更多