具有高阶函数的递归

时间:2019-10-07 18:15:31

标签: javascript recursion ecmascript-6

我想了解以下示例,因此对我来说很清楚。不幸的是,我的头挂在了线上:.forEach(c =>(node [c.id] = makeTree(categories,c.id)))。有人可以给我提示吗?

let categories = [
  { id: 'animals', parent: null },
  { id: 'mammals', parent: 'animals' },
  { id: 'cats', parent: 'mammals' },
  { id: 'dogs', parent: 'mammals' },
  { id: 'chihuahua', parent: 'dogs' },
  { id: 'labrador', parent: 'dogs' },
  { id: 'persian', parent: 'cats' },
  { id: 'siamese', parent: 'cats' }
];

let makeTree = (categories, parent) => {
  let node = {};
  categories
    .filter(c => c.parent == parent)
    .forEach(c => (node[c.id] = makeTree(categories, c.id)));
  return node;
};

console.log(makeTree(categories, null));

expected:

{
  animals: {
    mammals: {
      dogs: {
        chihuahua: null
        labrador: null
      },
      cats: {
        persian: null
        siamese: null
      }
    }
  }
}

3 个答案:

答案 0 :(得分:4)

可以等效地(并且,恕我直言,更干净)用普通循环和有条件的代码(而不是filterforEach)来编写代码:

function makeTree(categories, parent) {
  let node = {};
  for (const c of categories)
    if (c.parent == parent)
      node[c.id] = makeTree(categories, c.id);
  return node;
}

现在,它只是一个普通的递归函数,没有高阶的东西了。

另外,对于forEach回调,它在grouping parenthesis中使用了完全不必要的shorthand arrow function syntax,而不是用块体正确地写它(因为不需要从代码块返回任何内容) forEach回调):

.forEach(c => {
  node[c.id] = makeTree(categories, c.id);
});

答案 1 :(得分:1)

这更有意义吗?

小证明:https://jsfiddle.net/gz6uyodw/2/

function makeTree(categories, parent) {
  let node = {};
  const filteredArr = categories.filter(c => c.parent === parent);
  console.log('level arr', filteredArr)
  for (let obj of filteredArr) {
    const nodeToAppend = makeTree(categories, obj.id);
    console.log('node to append:', nodeToAppend)
    node[obj.id] = nodeToAppend;
  }
  return node;
}

console.log(makeTree(categories, null));

基本来说,您正在准备一个要为其准备级别的过滤数组,对于第一个级别,您的动物包含第二个filteredArr级别的哺乳动物,哺乳动物的组为狗,并且其中有2个对象。数组(添加了一个额外的数组,以便为​​猫和狗设置不同的filteredArr),等等。

答案 2 :(得分:1)

递归只是花哨的循环。

使递归难以理解的原因是循环的一部分对您隐藏了。

隐藏的部分称为调用堆栈。了解调用堆栈,您就会了解递归。

function makeTree(categories, parent) {
  let node = {};
  const stack = [{ parent, node }];
  while (stack.length) {
    const { parent, node } = stack.pop();
    for (const category of categories) {
      if (category.parent === parent) {
        const subnode = {};
        node[category.id] = subnode;
        stack.push({
          parent: category.id,
          node: subnode
        });
      }
    }
  }
  return node;
}

let categories = [
  { id: 'animals', parent: null },
  { id: 'mammals', parent: 'animals' },
  { id: 'cats', parent: 'mammals' },
  { id: 'dogs', parent: 'mammals' },
  { id: 'chihuahua', parent: 'dogs' },
  { id: 'labrador', parent: 'dogs' },
  { id: 'persian', parent: 'cats' },
  { id: 'siamese', parent: 'cats' }
];

document.body.innerHTML = `<pre>${JSON.stringify(makeTree(categories, null), null, 2)}</pre>`

更长一些,但确切地说是递归的工作方式:

function makeTree(categories, parent) {
  const stack = [{ parent }];
  let subnode; // the return value
  call: while (stack.length) {
    let { parent, node, i, c } = stack.pop();
    if (!node) {
      node = {};
      i = 0;
    } else {
      node[c.id] = subnode;
    }
    for (; i < categories.length; i++) {
      const category = categories[i];
      if (category.parent === parent) {
        stack.push({
          parent,
          node,
          i: i+1,
          c: category
        });
        stack.push({
          parent: category.id
        });
        continue call;
      }
    }
    subnode = node;
  }
  return subnode;
}