繁体   English   中英

有关“尾叫优化”文章的问题

[英]Question on “Tail Call Optimization” Article

我对这篇文章有疑问。

这段代码之间

function odds(n, p) {
  if(n == 0) {
    return 1
  } else {
    return (n / p) * odds(n - 1, p - 1)
  }
}

和这段代码

(function(){
  var odds1 = function(n, p, acc) {
    if(n == 0) {
      return acc
    } else {
      return odds1(n - 1, p - 1, (n / p) * acc)
    }
  }

  odds = function(n, p) {
    return odds1(n, p, 1)
  }  
})()

1)我对此有多大困惑。 第二个代码段是否只是简单地进行了尾部调用,因为它可以在再次调用自身之前计算出所需的内容,从而减少了开销,还是我还缺少更多的功能?

据我了解,尾调用仍然没有消除,只是优化了。

2)为什么不应该有一定oddsodds1呢? 我还是不清楚。

我对这有多大帮助感到困惑。 第二个代码段是否只是简单地进行了尾部调用,因为它可以在再次调用自身之前计算出所需的内容,从而减少了开销,还是我还缺少更多的功能?

据我了解,尾调用仍然没有消除,只是优化了。

如果过程结束看起来像这样:

push args
call foo
return

然后编译器可以将其优化为

jump startOfFoo

完全消除过程调用。

为什么仍然需要几率? 我还是不清楚。

odds的“合同”仅指定两个参数-第三个参数仅是实现细节。 因此,您可以将其隐藏在内部方法中,并提供“包装器”作为外部API。

你可以称之为odds1oddsImpl ,它会更清楚,我想。

第一个版本不是尾部递归,因为在获得odds(n - 1, p - 1)的值之后odds(n - 1, p - 1)它必须将其乘以(n / p) ,第二个版本将其移到参数的计算中函数odds1使它正确地尾部递归。

如果您查看调用堆栈,则第一个调用将如下所示:

odds(2, 3)
  odds(1, 2)
    odds(0, 1)
    return 1
  return 1/2 * 1
return 2/3 * 1/2

而第二个是:

odds(2, 3)
  odds1(2, 3, 1)
    odds1(1, 2, 2/3)
      odds1(0, 1, 1/2 * 2/3)
      return 1/3
    return 1/3
  return 1/3
return 1/3

因为您只是返回递归调用的值,所以编译器可以轻松优化此值:

odds(2, 3)
#discard stackframe
odds1(2, 3, 1)
#discard stackframe
odds1(1, 2, 2/3)
#discard stackframe
odds1(0, 1, 1/3)
return 1/3

具有oddsodds1的原因odds1是在其他代码调用此函数时提供初始累加器值。

尾递归的优化如下,在第一个示例中,因为直到调用odds(n-1),您才能计算乘法return (n / p) * odds(n - 1, p - 1)的结果return (n / p) * odds(n - 1, p - 1) -1) ,操作者必须将我们当前的位置保存在内存中(在堆栈上),并打开一个新的赔率电话。

递归地,这也将在下一个调用中以及随后的调用中发生,依此类推。 因此,到递归结束并开始返回值并计算乘积时,我们将有n个待处理操作。

在第二个示例中,由于执行的return语句只是return odds1(n - 1, p - 1, (n / p) * acc)我们可以计算函数参数,而无需保持就可以简单地调用odds1(n-1) 我们目前的职位 这就是优化所在,因为现在我不必每次在堆栈上打开新框架时都记得自己在哪里。

可以将其视为书籍参考。 想象您打开一本食谱并转到某个食谱,其食材如下所示:

  1. 下一页的成分

下一页有

  1. 胡椒
  2. 下一页的成分

等等。你怎么知道所有成分是什么? 您必须记住在每一页上看到的内容!

尽管第二个示例更像以下成分列表:

  1. 忘记这一点,只需使用下一页上的内容即可

下一页有:

  1. 胡椒
  2. 忘记这一点,只需使用下一页上的内容即可

等等。等到到达最后一页时(注意,类推是准确的,因为两者都调用相同数量的函数),您便拥有了所有要素,而不必“保留在内存中”在每一页上看到的内容,因为所有内容都在最后一页!

暂无
暂无

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

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