阶乘尾调用的延续样式实现是否优化 (TCO)?



以下是本网站的两个阶乘实现:

尾部调用优化 (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并且根据本教程,它的调用不在尾部位置。

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

带有--harmony_tailcalls的 Node7 不认为第二个对 TCO 有效,但 TCO 在 V8 中不是 100% 完成的(因此在运行时标志后面)。

Axel Rauschmayer说不,这不是尾部调用,因为有一个隐含的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可能会发出一个值不是undefinedreturn,而foo根本不返回任何值(因此调用它保证产生undefined)。当我们打电话给bar说"但不要返回bar的返回值"时,必须留下一些东西(至少目前是这样)。 那个东西是一个堆栈帧。

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

最新更新