重构递归函数

时间:2014-04-28 18:44:55

标签: javascript recursion

我曾多次尝试重构这个以使用迭代而不是递归,但我无法绕过它。也许它与循环中发生的递归有关。协助,甚至只是伪代码,将非常感激。

var getElementAndDescendants = function (el) {
    var docFrag = document.createDocumentFragment();

    var children = el.parentElement.querySelectorAll("tr[data-parentid='" + el.getAttribute("data-myid") + "']");

    docFrag.appendChild(el);

    var len = children.length;
    for (var index = 0; index < len; index++) {
        docFrag.appendChild(getElementAndDescendants(children[index]));
    }

    return docFrag;
};

更新:这只是一个较大的函数的一小部分,它试图对由在同一个表中有子TR的TR构成的DOM中的伪树进行排序。每个孩子都可以有自己的孩子。解决方案最终成为一个递归函数,其中包含您在此处看到的递归函数。因此,为什么我在微优化之后(如果有的话)。我试图让问题变得简单,从而删除了外部函数。

2 个答案:

答案 0 :(得分:3)

就像@Bergi已经说过的那样,从递归到迭代可能不会显着提高性能(或者可能会更慢......得到jsperf!)。抛弃递归的主要原因是当你处理大树时遇到一些堆栈溢出问题。

您绝对应该避免将数据存储在DOM中。

但是,这是我为您创建的深度优先树迭代示例:

var tree = {
    title: 'root',
    children: [
        { title: 'node 1' },
        { 
                    title: 'node 2',
                    children: [
                        { title: 'node 5' },
                        { title: 'node 6' }
                    ] 
                },
        { title: 'node 3' },
        { 
            title: 'node 4',
            children: [
                { title: 'node 7' }
            ]
        }
    ]
};

function dfsIterativePrintTree(node) {
    var queue = [node], i, children;

    while (node = queue.pop()) {        
        children = node.children || [];         
        //loop backward, otherwise the nodes will be traversed backward because
        //we pop the nodes.
        for (i = children.length; i--;) queue.push(children[i]);

        console.log(node.title);
    }
}

dfsIterativePrintTree(tree);

答案 1 :(得分:1)

以下内容应该有效,但将其视为伪代码。

function getElementAndDescendants(el) {
  /* Keep a last in first out list of elements to "solve." */
  var stack = [];
  /* Store solutions here. */
  solutions = {};

  stack.push(el);

  while (stack.length != 0) {
    var el = stack.pop();

    var docFrag = document.createDocumentFragment();
    var children = el.parentElement.querySelectorAll("tr[data-parentid='" + el.getAttribute("data-myid") + "']");
    var children_len = children.length;

    if (!el in solutions && children_len != 0) {
      stack.push(el);

      /* This way, next time we get to me, we know my children were queued
       * up earlier and must have been solved. */
      solutions[el] = null;

      /* My children are not solved; queue them and solve them first. */
      for (var i = 0; i < children_len; ++i) {
        stack.push(children[i]);
      }
    } else {
      /* Me and my children are solved! Pull me off the stack and solve. */
      stack.pop();

      docFrag.appendChild(el);

      /* The children must have been solved, if there are any. */
      for (var i = 0; i < children_len; ++i) {
        docFrag.appendChild(solutions[el]);
      }

      solutions[el] = docFrag;
    }
  }
}