简体   繁体   English

尾递归减少函数返回[…,[Curcular]]

[英]Tail recursive reduce function returns […, [Curcular] ]

Trying to Write a reduce function that would filter out any duplicates. 尝试编写一个reduce函数,该函数将过滤掉所有重复项。 I know there are other ways to solve this, but I'm trying to practice recursive functions. 我知道还有其他方法可以解决此问题,但是我正在尝试练习递归函数。

 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] } 

I read that circular means that object is self-referencing? 我读过通函意味着对象是自引用的? but I am not sure I fully understand what that means in the context of a Set. 但是我不确定我完全理解Set上下文的含义。 Why am I having this issue, and how would I solve it? 为什么会有这个问题,我将如何解决? Thanks so much for your time guys! 非常感谢您的宝贵时间!

A good way to think about this is to just look at the return value of addToSet . 考虑这一点的一种好方法是仅查看addToSet的返回值。 It returns the passed in set…every time. 每次返回传入的集合。 Now look at the return value of of reduce . 现在来看reduce的返回值。 It returns the result of fn which we just established always returns the set. 它返回我们刚刚建立的fn的结果,总是返回集合。

So the you pass the result of reduce into the second argument of fn at some point you will be passing the set into the second argument fn which will add the set to the set and give you a circular ref. 因此,您将reduce的结果传递到fn的第二个参数时,您将集合传递到第二个参数fn ,这会将集合添加到集合中并为您提供循环引用。

This: 这个:

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

eventually becomes: 最终成为:

 return fn(init, init)

It's not hard to solve, because there's no real reason to pass call the function twice. 解决起来并不难,因为没有真正的理由两次调用函数。 Your base case will return the set in the end, so you can just call fn once and return the result of reduce . 您的基本情况将最终返回集合,因此您只需调用一次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 

To figure out what's going on here, we can place a console log inside of your recursive function and run it with a small set like so: 为了弄清楚这里发生了什么,我们可以在您的递归函数中放置一个控制台日志,并使用如下这样的小集来运行它:

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)

We get this output (remember that the last line is what returns from the initial call at the end) 我们得到此输出(请记住,最后一行是从最后一次调用返回的内容)

在此处输入图片说明

As you can see, you call your recursive function one last time on the empty array and return init there, which gets added to the end of your set. 如您所见,您最后一次在空数组上调用递归函数,然后在其中返回init,并将其添加到集合的末尾。 You probably want to catch that by amending your base case. 您可能想通过修改基本案例来解决这一问题。 I'll leave that as an exercise for you to figure out, but if you need more help, you can always comment back. 我将其留给您练习,但是如果您需要更多帮助,可以随时提出反馈。

One more thought: 还有一个想法:

Consider that recursion is like saying that one run of the function is going to be responsible for one action/calculation/step/however you want to think about it. 考虑到递归就像说函数的一次运行将负责一个动作/计算/步骤/无论您想考虑什么。 Ask yourself what that one step is. 问问自己那一步是什么。

Ex: 例如:

If I am one function call, maybe I only want to be responsible for the question "do I add the current head to init ?" 如果我是一个函数调用,也许我只想对以下问题负责:“我是否将当前head添加到init ?”

There are loads of ways to do this, but maybe one way would be to say (in pseudocode): 有很多方法可以做到这一点,但也许一种方法是说(用伪代码):

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
}

This doesn't account undefined actually being a value in the array, but hopefully it illustrates the concept. 这并不能说明undefined实际上是数组中的一个值,但希望它能说明这个概念。

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

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