【发布时间】: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/oreturn与return;或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