我一直在努力理解JavaScript上下文中的Tail call optimization
,并为factorial()
编写了以下递归和尾部递归方法。
递归:
function factorial (n) {
if (n < 2) {
return 1;
} else {
return n * factorial(n-1);
}
}
尾部递归:
function factorial (n) {
function fact(n, acc) {
if (n < 2) {
return acc;
} else {
return fact(n-1, n * acc);
}
}
return fact(n, 1)
}
但我不确定tail-recursive
版本的函数是否会像在Scala等其他语言中一样,通过JavaScript编译器进行优化。有人能帮我解决这个问题吗?
更新:截至2023年7月22日,Safari是唯一支持尾部调用优化的浏览器
如果目标是简单地绕过堆栈限制,就像其他答案所显示的那样,这是可能的。所需要的只是一个懒惰的功能和一个蹦床这与尾部调用优化不同
const trampoline = (x) => {
while (typeof x == 'function') x = x()
return x;
}
const lazy = (f) => (...args) => () => f(...args)
function factorial (n) {
const f = lazy((a, n) => n == 0 ? a : f(n * a, n - 1));
return trampoline(f(1, n));
}
console.log(factorial(30000)); // Infinity
chromium团队明确表示,Tail Call Optimization没有在积极开发中,可以在这里进行跟踪。
Firefox的实现可以在这里跟踪
原始帖子
是的,ES2015在严格模式下提供尾呼优化。Axel Rauschmayer博士在下面的链接中完美地阐述了这一点,所以我不在这里重复他的话。
注意:ES 5不会优化尾部调用。
http://www.2ality.com/2015/06/tail-call-optimization.html
正如其他答案所说,不是在实践中。但是,您可以定义一个实用程序来提供帮助。
class Tco {
constructor(func) {
this.func = func;
}
execute() {
let value = this;
while (value instanceof Tco)
value = value.func();
return value;
}
}
const tco = (f) => new Tco(f);
function factorial (n) {
const fact = (n, acc) => tco(() => {
if (n < 2) {
return acc;
} else {
return fact(n-1, n * acc);
}
});
return fact(n, 1).execute();
}
console.log(factorial(2000000)); // Infinity
正如您所看到的,这允许您编写语法上只有很小差异的尾部递归函数,而不会遇到最大调用堆栈错误。
理论上是的。正如另一个答案所说。
然而,在实践中,截至2017年7月,没有。只有Safari支持它。
Javascript ES6(ES2015)兼容性:https://kangax.github.io/compat-table/es6/
Safari是唯一支持尾部调用优化的浏览器。ES2015在严格模式中提供尾呼优化
function factorial(n, returnVal= 1) {
'use strict';
if (n <= 1) return returnVal;
return factorial(n - 1, n * returnVal);
}
factorial(555)
按照链接
截至2023年6月,大多数发动机仍然看不到对TCO的支持。
那些想要实现尾部递归算法并对其进行优化的人,可以使用一个实用程序来实现,例如https://stackoverflow.com/a/62376811/7379821
我实现了一个与npm包类似的东西:
https://www.npmjs.com/package/@xtao org/tailrec.js
它的好处是,您可以正常地调用尾部递归函数——您只需在正文中标记尾部调用,如下所示:
import tailrec from "@xtao-org/tailrec.js"
function factorial(n) {
const fact = tailrec((n, acc) => {
if (n < 2) {
return acc;
} else {
return fact.tail(n-1, n * acc);
}
})
return fact(n, 1)
}
我希望这能让某人的生活更轻松。享受