How to prevent a recursive function from re-initializing an accumulating variable?

心已入冬 提交于 2021-02-09 08:51:25

问题


This function is written in JavaScript but I think that the concept can be implemented with some other programming languages.

function uniteUnique(arr) {
    let seenBefore = []; //the accumulating array
    for (let item of arguments) {
        if (typeof (item) == "object") {
            uniteUnique(...item);
        }
        else if (!seenBefore.includes(item)) {
            seenBefore.push(item);
        }
    }
    return seenBefore;
}

In short, the function iterates over arrays it receives as arguments, which may or may not contain other arrays themselves. the deepest level of any of those arrays contains int values. The function returns an array that contains all those ints (i.e those that appeared in nested arrays), but it returns each int only once, even if it appeared more than one time.

My problem lies in the fact that every time the recursion returns to a higher level, it initializes again the array that contains the saved ints, namely the array that the function needs to return (seenBefore), and therefore ruins the whole process. On one hand, I must initialize the array when the function starts, but on the other hand, it is initialized more than once and loses its previously stored values.

for example, if I would run the function

uniteUnique([1, 3, [6, 3], 2], [5, 2, 1, 4], [2, 1]);

the output should be

[1,3,6,2,5,4]

because the function has to return the numbers it stumble upon only once by the order of processing. The function actually returns an empty array because it is initialized again right before the function returns from the top-most level of the recursion.

How can I bypass this problem?

(P.S: I know this can be "solved" by pulling the accumulating array out of the function to a different scope, but that causes other problems, such as the need to reinitialize the accumulating array each time before I run the function if I run it more than once.)


回答1:


You've misidentified your problem. Each call to uniteUnique() has a separate value for the local variable seenBefore -- nothing is being "initialized again" during recursive calls.

Your real problem is that the line:

uniteUnique(...item);

discards the result of that function call, so the contents of any nested arrays are ignored. You need to assign the return value of this function somewhere and use it.

You may also want to change the condition for this function call to:

if (Array.isArray(item)) {

as the current condition typeof item == "object" will include objects which cannot be iterated over.




回答2:


Another possibility would be to define seenBefore as the empty array as a default second parameter, which is recursively passed to every call of the function:

function uniteUnique(arr, seenBefore = []) {
  for (const item of arr) {
    if (typeof (item) == "object") {
      uniteUnique(item, seenBefore);
    }
    else if (!seenBefore.includes(item)) {
      seenBefore.push(item);
    }
  }
  return seenBefore;
}

uniteUnique(someArr);

Note that this accepts a single argument as an array, rather than multiple arguments.




回答3:


You can utilize a nested function, so that you don't need to reinitialize the accumulating array each time:

function uniteUnique(arr) {
    function iterate(seenBefore, arr)
    {
        for (let item of arr) {
            if (Array.isArray(item)) {
                iterate(seenBefore, item);
            }
            else if (!seenBefore.includes(item)) {
                seenBefore.push(item);
            }
        }
    }

    let result = []; // the accumulating array
    iterate(result, arr);
    return result ;
}

You don't actually need to use arguments and spread operator here, because your function expects an array and you can simply pass an array.

You may also want to use Set for seenBefore, because Array.prototype.includes scans through an array, which is ineffective.




回答4:


Just in case: if you can use the last ES2019 features and prefer simplicity, this can also be achieved with a one-liner:

const uniquesArray = [...new Set(nestedArray.flat(Infinity))];


来源:https://stackoverflow.com/questions/54412311/how-to-prevent-a-recursive-function-from-re-initializing-an-accumulating-variabl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!