连续传递样式导致 "Maximum call stack size exceeded " 错误
Continuation-passing style causes "Maximum call stack size exceeded " error
我试图在 typescript 中使用“Continuation-passing style”来解决“fibonacci”序列,这是一个非常简单的例子,但只是为了尝试
const cpsFib = (n: number, f: (t: number) => void) => {
if(n === 0 || n === 1) f(1);
else {
cpsFib(n - 1, (t0) => {
cpsFib(n - 2, (t1) => {
f(t0 + t1);
});
})
}
}
const num = 16;
console.time('cps');
const ans = cpsFib(num, (ans) => console.log(`fib(${num}): ${ans}`));
console.timeEnd('cps');
问题是当我将 16
作为输入时,它会导致
Maximum call stack size exceeded
但是当我尝试使用 direct style
并提供超过 40
的输入时,它工作得很好
const num = 40;
const fib: (arg: number) => number = (n: number) => n <= 1 ? 1 : fib(n-1)+fib(n-2);
console.log(`fib(${num}): ${fib(num)}`);
所以我想深入了解为什么 CPS
不能用超过 16
解决它,而直接样式可以。
据我所知,CPS
为每个函数传递打开内存中的堆栈,direct style
为每个函数调用打开内存中的堆栈。
嗯,direct 风格只在堆栈上存储 n
,而 CPS 风格存储 n
和一个 lambda。我不知道打字稿是如何在内存中存储 lambda 的,但至少它需要 t
和一个函数指针,所以每次调用至少需要 3 倍的堆栈量 space.
更重要的是,由于嵌套,CPS 解决方案的每个递归步骤也会使堆栈更深,直接样式在堆栈中下降 f(n-1)
,然后 备份 堆栈,然后再次下降 f(n-2)
。
通常要使 CPS 堆栈安全,您必须实现蹦床或使用具有尾调用优化的语言实现。
我试图在 typescript 中使用“Continuation-passing style”来解决“fibonacci”序列,这是一个非常简单的例子,但只是为了尝试
const cpsFib = (n: number, f: (t: number) => void) => {
if(n === 0 || n === 1) f(1);
else {
cpsFib(n - 1, (t0) => {
cpsFib(n - 2, (t1) => {
f(t0 + t1);
});
})
}
}
const num = 16;
console.time('cps');
const ans = cpsFib(num, (ans) => console.log(`fib(${num}): ${ans}`));
console.timeEnd('cps');
问题是当我将 16
作为输入时,它会导致
Maximum call stack size exceeded
但是当我尝试使用 direct style
并提供超过 40
的输入时,它工作得很好
const num = 40;
const fib: (arg: number) => number = (n: number) => n <= 1 ? 1 : fib(n-1)+fib(n-2);
console.log(`fib(${num}): ${fib(num)}`);
所以我想深入了解为什么 CPS
不能用超过 16
解决它,而直接样式可以。
据我所知,CPS
为每个函数传递打开内存中的堆栈,direct style
为每个函数调用打开内存中的堆栈。
嗯,direct 风格只在堆栈上存储 n
,而 CPS 风格存储 n
和一个 lambda。我不知道打字稿是如何在内存中存储 lambda 的,但至少它需要 t
和一个函数指针,所以每次调用至少需要 3 倍的堆栈量 space.
更重要的是,由于嵌套,CPS 解决方案的每个递归步骤也会使堆栈更深,直接样式在堆栈中下降 f(n-1)
,然后 备份 堆栈,然后再次下降 f(n-2)
。
通常要使 CPS 堆栈安全,您必须实现蹦床或使用具有尾调用优化的语言实现。