首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用setImmediate()进行递归

用setImmediate()进行递归
EN

Stack Overflow用户
提问于 2017-10-15 08:38:39
回答 1查看 922关注 0票数 0

我有以下递归函数:

代码语言:javascript
复制
 function explore(v, componentN) {
    return function () {
        nodes[v].visited = true;
        nodes[v].componentNumber = componentN;
        preVisit(v);
        adjList[v].forEach((item) => {
            if (!nodes[item].visited){
                ex(item, componentN)
            }
        });
        postVisit(v);
        return nodes;
    }
}
function ex(item, com){
    trampoline(function () {
        return explore(item, com)
    })
}
function trampoline(fn) {
    while(fn && typeof fn === 'function') {
        fn = fn()
    }
}

为了避免堆栈溢出问题,我希望使用setImmediate() (它应该以这种方式实现,而不是在迭代方法中实现)。但是,当我执行这个函数时,我只使用第一个元素获得数组res,而如果我不使用setImmediate(),则得到它包含的所有元素,并且在使用nextTick()时也会出现相同的情况(我事先知道应该有多少个元素)。我做错了什么,如何在这里正确地实现这个函数(或模拟)?

这是使用蹦床前的explore()函数。

代码语言:javascript
复制
function explore(v, componentN) {
    nodes[v].visited = true;
    nodes[v].componentNumber = componentN;
    preVisit(v);
    adjList[v].forEach((item) => {
        if (!nodes[item].visited){
            explore(item, componentN)
        }
    });
    postVisit(v);
    return nodes;
}

它接受两个参数:vnodes数组中的索引,应该是探索元素;componentN是图中组件的计数器。explore()不返回任何内容,它只是将数组nodesv键下的对象的状态从未探索的更改为探索的对象。主要问题是,两个函数preVisit(v)postVisit(v)也改变了对象的状态--第一次和第二次访问对象的写入顺序(从以前的调用中弹出堆栈时)。当我用蹦床改造explore()时,他们开始写出意想不到的错误结果。

在另一个函数中以这种方式调用该函数:

代码语言:javascript
复制
for (let i = 0; i < vNum; i++) {
        if (nodes[i].visited) continue;
        explore(i, cN);
        cN++;
    }

和双功能postVisitpreVisit

代码语言:javascript
复制
function preVisit(v){
    nodes[v].preVisit = visitCounter;
    visitCounter++;
}
function postVisit(v) {
    nodes[v].postVisit = visitCounter;
    visitCounter++;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-10-27 23:49:24

假设我们有一个像这样的普通递归函数--这里的问题是,我们有一个forEach循环,每个对explore的调用都可以产生0、1或更多的对explore的额外调用--为了解决这个问题,我们需要一些方法来对所有节点进行排序,这样我们就可以一个一个地处理它们,而不会炸掉堆栈。

代码语言:javascript
复制
const explore = node =>
  {
    node.visited = true
    preVisit (node)
    Array.from (node.adjacencies)
      .filter (n => n.visited === false)
      .forEach (n => explore (n))
    postVisit (node)
  }

我们在explore函数中引入一个主循环,它处理一个节点数组,而不仅仅是一个节点。当数组中有节点时,循环将继续运行。将对内循环函数而不是外部explore函数进行递归调用。这种数组方法工作得很好,因为每个节点的邻接量都不明确--当可以很容易地将0、1或更多的相邻节点添加到此列表时--还需要注意添加了连续的k,因此我们可以按照正确的顺序对postVisit调用进行排序。

最重要的是,对loop的递归调用处于尾部位置,这为下一次转换做好了准备。

代码语言:javascript
复制
const explore = node =>
  {
    const loop = ([x, ...xs], k) =>
      {
        if (x === undefined)
          k ()
        else {
          x.visited = true
          preVisit (x)
          loop (
            Array.from (x.adjacencies)
              .filter (n => n.visited === false)
              .concat (xs),
            () => (postVisit (x), k ())
          )
        }
      }
    return loop ([node], x => x)
  }

一旦我们有了一个尾递归循环,我们就可以引入loop/recur来进行堆栈安全递归。

代码语言:javascript
复制
const recur = (...values) =>
  ({ type: recur, values })

const loop = f =>
  {
    let acc = f ()
    while (acc && acc.type === recur)
      acc = f (...acc.values)
    return acc
  }

const explore = $node =>
  loop (([x,...xs] = [$node], k = x => x) => {
    if (x === undefined)
      return k ()
    else {
      x.visited = true
      preVisit (x)
      return recur (
        Array.from (x.adjacencies)
          .filter (n => n.visited === false)
          .concat (xs),
        () => (postVisit (x), k ())
      )
    }
  })

// for completion's sake
let visitCounter = 0

const preVisit = node =>
  node.preVisit = visitCounter++

const postVisit = node =>
  node.postVisit = visitCounter++
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46753241

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档