【问题标题】:Is continuation style implementation of factorial tail call optimized (TCO)?阶乘尾调用的延续样式实现是否优化 (TCO)?
【发布时间】:2017-04-10 08:08:14
【问题描述】:

这里有两个阶乘实现from this site

优化尾调用 (TCO):

function fact(n) {
  return tail_fact(n,1) ;
}

function tail_fact(n,a) {
  if (n == 0)
    return a ;
  else
    return tail_fact(n-1,n*a) ;
}

还有一个用延续式编程(回调)重写的:

function fact(n,ret) {
  tail_fact(n,1,ret) ;
} 

function tail_fact(n,a,ret) {
  if (n == 0)
    ret(a) ;
  else
    tail_fact(n-1,n*a,ret) ;
}

看来教程建议第二个也是TCO,但是第二个版本返回的最后一个东西是undefined,根据this tutorial,它的调用不在尾部位置。

然而,这里似乎根本没有使用return,因此不需要在堆栈上创建一个带有返回地址的新帧。那么这就是第二次实施 TCO 的原因吗?

【问题讨论】:

  • Node7 与 --harmony_tailcalls 不认为第二个对 TCO 有效。 Axel Rauschmayer says no,这不是尾号,因为后面有一个隐含的return undefined;。我不确定我是否认同这种逻辑(但实际上不同意 Axel R 让我非常紧张); JS 区分退出函数 w/o returnreturn;return undefined;,即使调用它们的结果是相同的。在某个时候,我会拿出我的砍刀并按照规格...
  • 如果我们可以将tail_fact 的返回类型声明为void(未定义),我们可以简单地看到return 不会改变任何东西(仍然返回未定义)并且call 处于尾部位置。不幸的是,我们在 JS 中没有静态类型,因此您需要显式添加 return希望它总是返回 undefined...
  • 我猜一个聪明的解释器也可以对第二个版本进行尾部优化 - 它在返回位置之后放置一个return <constant>,并且当下一个堆栈帧执行相同操作时,它知道返回值将被忽略并且可以避免内部堆栈帧的创建。但肯定 ES6 规范不需要这个……
  • @T.J.Crowder,谢谢,您是如何发现Node 不认为第二个对 TCO 有效
  • @T.J.Crowder,@Bergi,谢谢大家,所以现在我可以得出结论,JS 中的 TCO 很可能无法保证正确吗?

标签: javascript tail-call-optimization


【解决方案1】:

--harmony_tailcalls 的 Node7 认为第二个对 TCO 无效,但在 V8 中 TCO 并非 100% 完成(因此落后于运行时标志)。

Axel Rauschmayer says no, it's not a tail call,因为有一个隐含的return undefined;之后:

以下代码中的函数调用bar()不在尾部位置:

function foo() {
    bar(); // this is not a tail call in JS
}

原因是foo()的最后一个动作不是函数调用bar(),它是(隐式)返回undefined。换句话说,foo() 的行为如下:

function foo() {
    bar();
    return undefined;
}

调用者可以依赖foo() 始终返回undefined。如果 bar() 要返回 foo() 的结果,由于尾调用优化,那么这将改变 foo 的行为。

换句话说,在foo 的末尾,我们不能只跳转到bar,因为bar 可能会发出return 的值不是undefined,而foo根本不返回任何值(因此调用它可以保证产生undefined)。当我们调用bar 说“但不返回bar 的返回值”时,有些东西 必须留下(至少现在)。那是一个堆栈帧。

理论上,JavaScript 引擎可以在调用 bar 时传递某种标志,告诉自己丢弃任何值 bar 返回,从而在这种情况下允许 TCO,但我不这样做'在规范中看不到任何这样做的东西。

【讨论】:

    猜你喜欢
    • 2021-05-27
    • 1970-01-01
    • 2011-04-09
    • 2021-05-15
    • 2020-10-21
    • 1970-01-01
    • 2010-10-23
    • 2012-04-18
    相关资源
    最近更新 更多