【问题标题】:What happens after non-tail recursion bottoms out非尾递归触底后会发生什么
【发布时间】:2018-07-26 21:53:12
【问题描述】:

我是递归新手,一直在研究非尾递归。我相信这是递归调用不在函数末尾的地方。我正在查看一些递归代码示例,我对递归调用之后的代码何时以及如何执行感到困惑。更具体地说,当程序的当前行到达递归调用时,是在再次真正执行整个函数之前执行的最后一段代码,还是当前执行行立即跳回函数的开头,仅在整个递归触底后执行剩余的代码?谁能解释一下这对我有什么作用?

【问题讨论】:

  • 用你的话来说:当前行跳回到开头
  • 对不起 - 我不明白 - 这是非尾递归的情况吗?
  • 任何递归都会发生这种情况
  • 好的...所以函数内递归调用后的最后一段代码只有在所有递归触底后才执行?
  • 是的,这与您调用的任何其他函数完全相同(当然不是异步的情况)

标签: recursion


【解决方案1】:

在这里想象一下这段代码:(我在这里使用的是 JS,但它适用于所有渴望的语言)

function add(a, b) {
  return a + b;
}

function sub(a, b) {
  return a - b;
}

function fib(n) {
  if (n < 2) {
    return n;
  } else {
    return add(
      fib(sub(n, 2)),
      fib(sub(n, 1)),
    );
  }
}

console.log(fib(10));

现在当n2 或更多时,我们将调用两个前辈的fib 的结果添加到n。一些调用之间的顺序是JS引擎的选择,但是sub需要在fib获取值之前被调用并返回,并且当两个参数都有结果时需要调用add。这可以使用延续传递样式重写为尾调用:

function cadd(a, b, k) {
  k(a + b);
}

function csub(a, b, k) {
  k(a - b);
}

function cfib(n, k) {
  if (n < 2) {
    k(n);
  } else {
    csub(n, 2, n2 =>
      cfib(n2, rn2 =>
        csub(n, 1, n1 =>
          cfib(n1, rn1 =>
            cadd(rn2, rn1, k)))));
  }
}

cfib(10, console.log);

在这里您可以看到引擎选择的顺序是从左到右的。每个调用都做一件事,然后使用该结果调用延续。另请注意,这永远不会返回任何内容。 "return" 只能通过 continuation 完成,但它的作用与前面的代码完全相同。

在许多语言中,它们会编译成相同的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-02
    • 2018-03-17
    • 2012-03-19
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多