Как предотвратить рекурсивную функцию от повторной инициализации накопительной переменной? - PullRequest
0 голосов
/ 29 января 2019

Эта функция написана на JavaScript, но я думаю, что концепция может быть реализована с некоторыми другими языками программирования.

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

Короче говоря, функция выполняет итерацию по массивам, которые она получает в качестве аргументов, которые могут содержать или не содержать сами другие массивы.самый глубокий уровень любого из этих массивов содержит значения int.Функция возвращает массив, который содержит все эти int s (то есть те, которые появились во вложенных массивах), но она возвращает каждое int только один раз, даже если оно появилось более одного раза.

Моя проблема заключается втот факт, что каждый раз, когда рекурсия возвращается на более высокий уровень, она снова инициализирует массив, который содержит сохраненные int s, а именно массив, который должна вернуть функция (seenBefore), и, следовательно,рушит весь процесс.С одной стороны, я должен инициализировать массив при запуске функции, но с другой стороны, он инициализируется более одного раза и теряет свои ранее сохраненные значения.

например, если бы я запустил функцию

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

, результат должен быть

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

, потому чтофункция должна возвращать числа, на которые она наткнулась, только один раз в порядке их обработки.Функция фактически возвращает пустой массив, потому что он снова инициализируется непосредственно перед тем, как функция возвращается с самого верхнего уровня рекурсии.

Как я могу обойти эту проблему?

(PS: я знаю, что это можно «решить», вытянув накопительный массив из функции в другую область, но это вызывает другие проблемы, такие как необходимость повторной инициализации накопительного массива каждый раз, прежде чем язапустить функцию, если я запустил ее несколько раз.)

Ответы [ 4 ]

0 голосов
/ 29 января 2019

На всякий случай: если вы можете использовать последние функции ES2019 и предпочитаете простоту, это также может быть достигнуто с помощью одной строки:

const uniquesArray = [...new Set(nestedArray.flat(Infinity))];
0 голосов
/ 29 января 2019

Другой возможностью было бы определить seenBefore как пустой массив в качестве второго параметра по умолчанию , который рекурсивно передается при каждом вызове функции:

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);

Обратите внимание, чтоэто принимает один аргумент в виде массива, а не несколько аргументов.

0 голосов
/ 29 января 2019

Вы можете использовать вложенную функцию, так что вам не нужно каждый раз заново инициализировать накопительный массив:

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

Вам на самом деле не нужно использовать arguments и оператор распространения,потому что ваша функция ожидает массив, и вы можете просто передать массив.

Вы также можете использовать Set для seenBefore, поскольку Array.prototype.includes просматривает массив, что неэффективно.

0 голосов
/ 29 января 2019

Вы неправильно определили свою проблему.Каждый вызов uniteUnique() имеет отдельное значение для локальной переменной seenBefore - во время рекурсивных вызовов ничего не "инициализируется снова".

Ваша настоящая проблема в том, что строка:

uniteUnique(...item);

отбрасывает результат этого вызова функции, поэтому содержимое любых вложенных массивов игнорируется.Вам нужно где-то присвоить возвращаемое значение этой функции и использовать его.

Вы также можете изменить условие для вызова этой функции на:

if (Array.isArray(item)) {

в качестве текущего условия typeof item == "object" будет включать объекты, которые не могут быть повторены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...