3
votes

Comment empêcher une fonction récursive de réinitialiser une variable cumulative?

Cette fonction est écrite en JavaScript mais je pense que le concept peut être implémenté avec d'autres langages de programmation.

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

En bref, la fonction itère sur les tableaux qu'elle reçoit comme arguments, qui peuvent ou non contenir eux-mêmes d'autres tableaux. le niveau le plus profond de l'un de ces tableaux contient des valeurs int . La fonction renvoie un tableau contenant tous ces int s (c'est-à-dire ceux qui sont apparus dans des tableaux imbriqués), mais elle ne renvoie chaque int qu'une seule fois, même s'il est apparu plus d'une fois.

Mon problème réside dans le fait qu'à chaque fois que la récursion revient à un niveau supérieur, elle initialise à nouveau le tableau qui contient les int sauvegardés, à savoir le tableau que le La fonction doit retourner ( vuBefore ), et donc ruine l'ensemble du processus. D'une part, je dois initialiser le tableau au démarrage de la fonction, mais d'autre part, il est initialisé plus d'une fois et perd ses valeurs précédemment stockées.

par exemple, si j'exécuterais la fonction

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

la sortie doit être

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

car la fonction doit renvoyer les nombres sur lesquels elle tombe une seule fois par ordre de traitement. La fonction renvoie en fait un tableau vide car elle est initialisée à nouveau juste avant que la fonction ne retourne du niveau le plus élevé de la récursivité.

Comment puis-je contourner ce problème?

(PS: je sais que cela peut être "résolu" en tirant le tableau accumulé hors de la fonction vers une portée différente, mais cela pose d'autres problèmes, comme la nécessité de réinitialiser le tableau accumulé à chaque fois avant J'exécute la fonction si je l'exécute plus d'une fois.)


0 commentaires

4 Réponses :


3
votes

Vous avez mal identifié votre problème. Chaque appel à uniteUnique () a une valeur distincte pour la variable locale vuBefore - rien n'est "initialisé à nouveau" pendant les appels récursifs.

Votre vrai problème est que la ligne:

if (Array.isArray(item)) {

ignore le résultat de cet appel de fonction, donc le contenu de tous les tableaux imbriqués est ignoré. Vous devez attribuer la valeur de retour de cette fonction quelque part et l'utiliser.

Vous pouvez également changer la condition de cet appel de fonction en:

uniteUnique(...item);

comme condition actuelle, typeof item == "object" inclura les objets qui ne peuvent pas être itérés.


2 commentaires

L'entrée est toujours un tableau, jamais un objet, mais merci pour le conseil, c'est bien plus net.


Votre réponse m'a aidé à repenser le problème, mais peu importe comment vous le considérez, nous avons à nouveau le même problème. Même si j'attribue le résultat de la fonction à une variable et que je l'utilise, cette variable résidera dans une portée différente lorsque j'appelle à nouveau la fonction de manière récursive. Il doit probablement être transmis et sauvegardé à chaque itération de la récursivité. @duskwuff



3
votes

Une autre possibilité serait de définir seenBefore comme le tableau vide comme second paramètre par défaut , qui est passé récursivement à chaque appel de la fonction:

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 commentaires

1
votes

Vous pouvez utiliser une fonction imbriquée, de sorte que vous n’avez pas besoin de réinitialiser le tableau d’accumulation à chaque fois:

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

Vous n’avez pas besoin d’utiliser des arguments code> et opérateur de propagation ici, car votre fonction attend un tableau et vous pouvez simplement passer un tableau.

Vous pouvez également utiliser Set pour seenBefore , car Array.prototype.includes parcourt un tableau, qui est inefficace.


1 commentaires

Merci pour les conseils! C'est la spécification que je suis également limitée, donc il doit en être ainsi.



0
votes

Juste au cas où: si vous pouvez utiliser les dernières fonctionnalités de l'ES2019 et préférez la simplicité, cela peut également être réalisé avec un one-liner:

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


0 commentaires