即使没有递归函数,JavaScript 堆栈也会溢出

问题描述

假设我们想通过使用 lambda 表达式作为代表我们想要添加新数据的位置的“洞”来构建大型结构。例如,我们在这里使用这个想法构建 [0,[1,[2,null]]]

builder_0 = hole => hole;                // hole => hole;
builder_1 = hole => builder_0([0,hole]); // hole => [0,hole];
builder_2 = hole => builder_1([1,hole]];
builder_3 = hole => builder_2([2,hole]]];

// prints [0,null]]],but will stack overflows if too many lines
console.log(JSON.stringify(builder_3(null)));

这很好用。我们也可以循环执行:

let builder = hole => hole;
for (let i = 0; i < 1000; ++i) {
  let last_builder = builder;
  builder = hole => last_builder([i,hole]);
};
console.log(builder(null));

这也有效,但如果限制大于 10000,此算法将堆栈溢出。问题在于,由于 last_builder([i,hole]) 未在 hole => ... 闭包内求值,因此它会构建大量未求值的 lambda 表达式,这些 lambda 表达式会迅速消耗整个堆栈。请注意,[0,null]]] 只是一个无用的示例,JavaScript 将无法使用上述基于洞的技术构建任何大型结构(想想树、JSON、不可变容器等)。

调用优化和蹦床在这里无济于事,因为我们甚至没有递归函数开始。有没有什么聪明的技巧可以让这种函数式习语在没有堆栈溢出的情况下工作?

解决方法

当您转译函数式代码时,您可以允许自己在 JS 部分中不“函数式”。所以你可以不用递归创建你需要的数据结构:

x=[0,1,2,3,4,9,8,15,14,5,6,25,12,35,16,7]
y=sorted(x)

for i in range(1,len(y)):
    if y[i]!=i:
        print(i-1)
        break

(请在浏览器的控制台上尝试,因为代码片段工具无法重现它)

,

我意识到正确的方法是蹦床。我们只是使用一个直接的 lambda 来包装结果:

builder_0 = hole => () => hole;                // hole => hole;
builder_1 = hole => () => builder_0([0,hole]); // hole => [0,hole];
builder_2 = hole => () => builder_1([1,[1,hole]];
builder_3 = hole => () => builder_2([2,[2,hole]]];

然后我们使用一个 while 循环来剥离层而不吹栈:

var result = builder(null);
while (typeof result === "function") {
  result = result();
}

这是一个完整的例子,它构建了一个包含 100000 个数字的链表:

let builder = x => x;
for (let i = 0; i < 100000; ++i) {
  let last_builder = builder;
  builder = x => (() => last_builder([i,x]));
};

var result = builder(null);
while (typeof result === "function") {
  result = result();
}

console.log(result);

它不会堆栈溢出。

,

你可以回避三个功能。

const
    identity = value => value,structure = (number,value) => [number,value],construct = (number,value) => number--
        ? construct(number,structure(number,value))
        : value;
    
console.log(JSON.stringify(construct(1000,null)));

,

我不确定这是否适合您的需求,但您可以运行“lambda 组合”,在数组上调用 reducer,其中包含要插入结构中的值。

System: OS: Windows 10 10.0.18363 CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz Memory: 7.55 GB / 15.81 GB Binaries: Node: 14.15.1 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD npm: 7.5.4 - C:\Program Files\nodejs\npm.CMD Browsers: Chrome: 89.0.4389.114 Edge: Spartan (44.18362.449.0) Internet Explorer: 11.0.18362.1 npmPackages: vite: ^2.1.3 => 2.1.3 为数组中的每个条目调用提供的函数,在变量中“累积”返回值

Reduce