简体   繁体   English

一般来说,是否有可能将递归 function 转换为在 JavaScript 中使用手动堆栈的一个?

[英]Is it possible, in general, to transform a recursive function in one that uses a manual stack in JavaScript?

Mind the following function:请注意以下 function:

function count(n) {
  if (n === 0) {
    return 0;
  } else {
    return 1 + count(n - 1);
  }
}

It is the simplest recursive function that counts from 0 to N .它是从0N计数的最简单的递归 function 。 Since JavaScript has a small stack limit, that function easily overflows.由于 JavaScript 的堆栈限制很小,因此 function 很容易溢出。 In general, any recursive function can be converted in one that uses a manual stack and, thus, can't stack overflow;通常,任何递归 function 都可以转换为使用手动堆栈的方法,因此不会堆栈溢出; but doing so is complex.但这样做很复杂。 Is it possible, for the general case, to convert a JavaScript recursive function in one that uses its own stack, without using a continuation-passing style?对于一般情况,是否可以在不使用延续传递样式的情况下将 JavaScript 递归 function 转换为使用自己的堆栈的一个? In other words, suppose we had written:换句话说,假设我们写了:

const count = no_overflow(function(count) {
  return function(n) {
    if (n === 0) {
      return 0;
    } else {
      return 1 + count(n - 1);
    }
  }
});

Is it possible to implement no_overflow in such a way that this new count function is equivalent to the old one, except without stack overflows?是否可以实现no_overflow使得这个新count function 等同于旧计数,除了没有堆栈溢出?

Notes:笔记:

  1. This is not about tail call optimization, since no_overflow should work for non-tail-recursive functions.尾调用优化无关,因为no_overflow应该适用于非尾递归函数。

  2. Trampolining is not helpful since, for the general case, it requires the function to be written in a continuation-passing style, which it isn't.蹦床没有帮助,因为对于一般情况,它需要 function 以连续传递样式编写,但事实并非如此。

  3. Writing the function with yield doesn't work either for a similar reason: you can't yield from inner lambdas.由于类似的原因,使用yield编写 function 也不起作用:您不能从内部 lambdas 中yield

  4. That no_overflow would, essentially, work like a stack-free Y-combinator.本质上, no_overflow将像无堆栈的 Y 组合器一样工作。

In JavaScript, calling a function f(x, y, ...) subjects us to the underlying implementation details of the stack and frames.在 JavaScript 中,调用 function f(x, y, ...)使我们了解堆栈和帧的底层实现细节。 If you recur using function application, you will absolutely, unavoidably run into a stack overflow.如果您重复使用 function 应用程序,您绝对会不可避免地遇到堆栈溢出。

However, if we can adopt a slightly different notation, such as call(f, x, y, ...) , we can control function application however we want -但是,如果我们可以采用稍微不同的符号,例如call(f, x, y, ...) ,我们可以控制 function 应用程序,但是我们想要 -

const add1 = x =>
  x + 1

const count = (n = 0) =>
  n === 0
    ? 0
    : call(add1, call(count, n - 1)) // <-- count not in tail pos

console.log(noOverflow(count(99999)))
// 99999

Implementing noOverflow is a wrapper around loop , defined in this Q&A -实施noOverflowloop的包装器,在此问答中定义 -

const noOverflow = t =>
  loop(_ => t)

Unsurprisingly this is a non-trivial problem but the answer(s) there should help detail the things you have to consider and some good test cases, should you choose to implement a solution of your own.不出所料,这是一个不平凡的问题,但如果您选择实施自己的解决方案,那里的答案应该有助于详细说明您必须考虑的事情和一些好的测试用例。

Expand the snippet below to verify the results in your browser -展开下面的代码段以验证浏览器中的结果 -

 const call = (f, ...values) => ({ type: call, f, values }) const recur = (...values) => ({ type: recur, values }) const identity = x => x const loop = (f) => { const aux1 = (expr = {}, k = identity) => expr.type === recur? call (aux, expr.values, values => call (aux1, f (...values), k)): expr.type === call? call (aux, expr.values, values => call (aux1, expr.f (...values), k)): call (k, expr) const aux = (exprs = [], k) => call ( exprs.reduce ( (mr, e) => k => call (mr, r => call (aux1, e, x => call (k, [...r, x ]))), k => call (k, []) ), k ) return run (aux1 (f ())) } const run = r => { while (r && r.type === call) r = r.f (...r.values) return r } const noOverflow = t => loop(_ => t) const add1 = x => x + 1 const count = (n = 0) => n === 0? 0: call(add1, call(count, n - 1)) console.log(noOverflow(count(99999))) // 99999

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM