Javascript 蹦床递归堆栈溢出

Javascript 蹦床递归堆栈溢出,javascript,recursion,ecmascript-6,Javascript,Recursion,Ecmascript 6,我有一个递归函数sum,它计算传递给它的所有数字的和 函数和(num1,num2,…nums){ 如果(nums.length==0){返回num1+num2;} 返回和(num1+num2,…nums); } 设xs=[]; 对于(设i=0;i ({type:recur,args}); //总数 常数和=xs=>{ 常数len=xs.length; 回路( (acc=0,i=0)=> i==len ?acc :重现(acc+xs[i],i+1)); }; //然后跑。。。 常量xs=数组(1

我有一个递归函数
sum
,它计算传递给它的所有数字的和

函数和(num1,num2,…nums){
如果(nums.length==0){返回num1+num2;}
返回和(num1+num2,…nums);
}
设xs=[];
对于(设i=0;i<100;i++){xs.push(i);}
log(sum(…xs));
xs=[];
对于(设i=0;i<10000;i++){xs.push(i);}
log(sum(…xs))

您可以更改
sum
签名以接受数组而不是不同数量的参数,并使用解构来保持语法/可读性与现有的类似。这“修复”了stackoverflow错误,但速度越来越慢:D

function _sum([num1, num2, ...nums]) { /* ... */ }
例如,如果遇到最大参数计数的问题,那么递归/蹦床方法可能会太慢,无法使用…

您可以更改
sum
签名以接受数组而不是不同数量的参数,并使用解构来保持语法/可读性与现有的类似。这“修复”了stackoverflow错误,但速度越来越慢:D

function _sum([num1, num2, ...nums]) { /* ... */ }

例如:,如果遇到最大参数计数的问题,那么递归/蹦床方法可能太慢,无法处理…

另一个答案已经用代码解释了这个问题。这个答案表明,蹦床对于大多数基于数组的计算来说速度足够快,并且提供了更高级别的抽象:

//蹦床
常量循环=f=>{
设acc=f();
while(acc&&acc.type==重复)
acc=f(…acc.args);
返回acc;
};
常量重复=(…参数)=>
({type:recur,args});
//总数
常数和=xs=>{
常数len=xs.length;
回路(
(acc=0,i=0)=>
i==len
?acc
:重现(acc+xs[i],i+1));
};
//然后跑。。。
常量xs=数组(1e5)
.fill(0)
.map((x,i)=>i);

console.log(sum(xs))另一个答案已经用您的代码解释了这个问题。这个答案表明,蹦床对于大多数基于数组的计算来说速度足够快,并且提供了更高级别的抽象:

//蹦床
常量循环=f=>{
设acc=f();
while(acc&&acc.type==重复)
acc=f(…acc.args);
返回acc;
};
常量重复=(…参数)=>
({type:recur,args});
//总数
常数和=xs=>{
常数len=xs.length;
回路(
(acc=0,i=0)=>
i==len
?acc
:重现(acc+xs[i],i+1));
};
//然后跑。。。
常量xs=数组(1e5)
.fill(0)
.map((x,i)=>i);

console.log(sum(xs))另一个答案指出了函数参数数量的限制,但我想谈谈您的蹦床实现。我们正在运行的长时间计算可能需要返回一个函数。如果使用
typeof res==“function”
,则无法再将函数计算为返回值

相反,用一些独特的标识符对蹦床变体进行编码

const bounce=(f,…args)=>
({tag:bounce,f:f,args:args})
const done=(值)=>
({tag:done,value:value})
康斯特蹦床=t=>
{while(t&&t.tag===bounce)
t=t.f(…t.args)
if(t&&t.tag===完成)
返回t值
其他的
抛出错误(`unsupported trampoline type:${t.tag}`)
}
在开始之前,让我们首先获得一个要修复的示例函数

const none =
  Symbol ()

const badsum = ([ n1, n2 = none, ...rest ]) =>
  n2 === none
    ? n1
    : badsum ([ n1 + n2, ...rest ])
我们将向它抛出一个
范围
,看看它是否工作

const range = n =>
  Array.from
    ( Array (n + 1)
    , (_, n) => n
    )

console.log (range (10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

console.log (badsum (range (10)))
// 55
但它能应付大联盟吗

console.log (badsum (range (1000)))
// 500500

console.log (badsum (range (20000)))
// RangeError: Maximum call stack size exceeded
到目前为止,请在浏览器中查看结果

const无=
符号()
常量badsum=([n1,n2=none,…rest])=>
n2==无
? n1
:badsum([n1+n2,…剩余])
常数范围=n=>
Array.from
(数组(n+1)
,(u,n)=>n
)
console.log(范围(10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
console.log(badsum(范围(1000)))
// 500500
console.log(badsum(范围(20000)))

//RangeError:超过了最大调用堆栈大小
另一个答案指出了函数参数数量的限制,但我想对您的trampoline实现进行说明。我们正在运行的长时间计算可能需要返回一个函数。如果使用
typeof res==“function”
,则无法再将函数计算为返回值

相反,用一些独特的标识符对蹦床变体进行编码

const bounce=(f,…args)=>
({tag:bounce,f:f,args:args})
const done=(值)=>
({tag:done,value:value})
康斯特蹦床=t=>
{while(t&&t.tag===bounce)
t=t.f(…t.args)
if(t&&t.tag===完成)
返回t值
其他的
抛出错误(`unsupported trampoline type:${t.tag}`)
}
在开始之前,让我们首先获得一个要修复的示例函数

const none =
  Symbol ()

const badsum = ([ n1, n2 = none, ...rest ]) =>
  n2 === none
    ? n1
    : badsum ([ n1 + n2, ...rest ])
我们将向它抛出一个
范围
,看看它是否工作

const range = n =>
  Array.from
    ( Array (n + 1)
    , (_, n) => n
    )

console.log (range (10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

console.log (badsum (range (10)))
// 55
但它能应付大联盟吗

console.log (badsum (range (1000)))
// 500500

console.log (badsum (range (20000)))
// RangeError: Maximum call stack size exceeded
到目前为止,请在浏览器中查看结果

const无=
符号()
常量badsum=([n1,n2=none,…rest])=>
n2==无
? n1
:badsum([n1+n2,…剩余])
常数范围=n=>
Array.from
(数组(n+1)
,(u,n)=>n
)
console.log(范围(10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
console.log(badsum(范围(1000)))
// 500500
console.log(badsum(范围(20000)))

//RangeError:超过了最大调用堆栈大小
这可能与您有关:第一个将在现代浏览器中工作,这要感谢尾部调用递归优化(tco)。我想问题在于参数溢出。@JonasW。我知道
tco
,但即使在现代浏览器上,它也不适合我。@JonasW。是的,这是ES6规范的一部分。这并不意味着浏览器符合它。这可能与您有关:第一个将在现代浏览器中工作,这要感谢尾部调用递归优化(tco)。我想问题在于参数溢出。@JonasW。我知道tco,但即使在网络上,它也不适用于我