【问题标题】:Tail recursive reduce function returns [..., [Curcular] ]尾递归 reduce 函数返回 [..., [Circular] ]
【发布时间】:2018-08-18 02:19:19
【问题描述】:

尝试编写一个可以过滤掉任何重复项的 reduce 函数。我知道还有其他方法可以解决这个问题,但我正在尝试练习递归函数。

function addToSet(a, b) {
  a.add(b);
  return a;
}

let set = new Set;

function reduce([head, ...last], fn, init) {
  if (head === undefined) return init;
  return fn(fn(init, head), reduce(last, fn, init))
}
const a = reduce([1, 2, 4, 6, 4, 3, 1, 2, 5, 1, 3, 4, 5, 7, 7], addToSet, set)

console.log(a)

// in node this returns // Set { 1, 2, 4, 6, 3, 5, 7, [Circular] }

我读到循环意味着对象是自引用的?但我不确定我是否完全理解这在 Set 的上下文中意味着什么。为什么我会遇到这个问题,我该如何解决? 非常感谢你们的时间!

【问题讨论】:

    标签: javascript node.js function recursion circular-reference


    【解决方案1】:

    考虑这个问题的一个好方法是查看addToSet 的返回值。它每次都返回传入的集合。现在看reduce 的返回值。它返回我们刚刚建立的fn 的结果总是返回集合。

    因此,您将reduce 的结果传递给fn 的第二个参数,在某些时候,您会将集合传递给第二个参数fn,这会将集合添加到集合中并给您一个循环参考。

    这个:

     return fn(fn(init, head), reduce(last, fn, init))
    

    最终变成:

     return fn(init, init)
    

    这并不难解决,因为没有真正的理由要传递两次调用函数。您的基本情况最终将返回集合,因此您只需调用一次fn 并返回reduce 的结果。

    function addToSet(a, b) {
        a.add(b);
      }
      
      let set = new Set;
      
      function reduce([head, ...last], fn, init) {
        if (head === undefined) return init
        fn(init, head)
        return reduce(last, fn, init)
      }
      const a = reduce([1, 2, 4, 6, 4, 3, 1, 2, 5, 1, 3, 4, 5, 7, 7], addToSet, set)
      
     console.log([...a]) // spreading because sets don't print here
      

    【讨论】:

    • 非常感谢,这完全有道理!
    【解决方案2】:

    要弄清楚这里发生了什么,我们可以在递归函数中放置一个控制台日志,并使用这样的小集合运行它:

    function addToSet(a, b) {
      a.add(b);
      return a;
    }
    
    let set = new Set;
    
    function reduce([head, ...last], fn, init) {
      console.log("head", head)
      console.log("last", last)
      console.log("init", init)
      if (head === undefined) return init;
      return fn(fn(init, head), reduce(last, fn, init))
    }
    const a = reduce([2, 4, 4], addToSet, set)
    
    console.log(a)
    

    我们得到这个输出(记住最后一行是最后从初始调用返回的内容)

    如您所见,您最后一次在空数组上调用递归函数并在那里返回 init,它被添加到集合的末尾。您可能想通过修改基本案例来抓住这一点。我将把它作为练习留给你来解决,但如果你需要更多帮助,你可以随时回复。

    再想一想:

    考虑到递归就像说函数的一次运行将负责一个动作/计算/步骤/无论您想如何考虑它。问问自己这一步是什么。

    例如:

    如果我是一个函数调用,也许我只想负责“我是否将当前的head 添加到init?”这个问题。

    有很多方法可以做到这一点,但也许一种方法是说(用伪代码):

    reduce([head, ...last], fn, init) {
      is_base_case (where head is undefined)?
        return // do nothing -- we don't want undefined to be in the set
      otherwise
        attempt to add head to init
      reduce(...) // call the next reduce fn -- responsible for the next head
      return init // init should either have the original set or the set + head
    }
    

    这并没有说明 undefined 实际上是数组中的一个值,但希望它能说明这个概念。

    【讨论】:

      猜你喜欢
      • 2015-11-09
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 2021-09-03
      • 2019-09-14
      • 2013-09-21
      相关资源
      最近更新 更多