4
votes

Comment utiliser la récursivité dans une boucle de puissance avec un nombre croissant de paramètres?

C'est un problème de Power Loop typique, et je n'ai besoin que d'un simple et élégant (compact ) solution ... Je vais montrer d'abord l'exemple de problème / solution avec les boucles for imbriquées . Supposons que je doive transformer ce morceau de code en une récursivité:

3 '000'
2 '00'
1 '0'
3 '001'
2 '01'
1 '1'
3 '010'
2 '10'
1 '0'
3 '011'
2 '11'
1 '1'
...

La sortie de 14 lignes uniques de cet exemple à 3 niveaux est

callManyTimes([2,2,2], show);

function callManyTimes(maxIndices, func) {
    doCallManyTimes(maxIndices, func, [], 0);
}

function doCallManyTimes(maxIndices, func, args, index) {
    if (maxIndices.length == 0) {
        let x = args.slice(0); // cloning
        while(x.length>0) {
          func(x); // why send array[array]?
          x.shift();
        }
    } else {
        var rest = maxIndices.slice(1);
        for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) {
            doCallManyTimes(rest, func, args, index + 1);
        }
    }
}

function show(...args) {
   if (typeof args[0] == 'object') args=args[0] // workaround... can optimize?
   let code = String( args.reduce( (ac,cur) => ''+ac+cur ) )
   console.log( code.length, code )
}


0 commentaires

4 Réponses :


3
votes

Vous pouvez utiliser une fonction qui prend un tableau temporaire pour les valeurs générées.

function show(...args) {
    let code = args.join('');
    console.log(code.length, code);
}

function callManyTimes(max, cb, items = []) {
    var i, temp;
    if (items.length === max.length) return;
    for (i = 0; i < max[items.length]; i++) {
        temp = items.concat(i);
        cb(...temp);
        callManyTimes(max, cb, temp);
    }
}

callManyTimes([2, 2, 2], show);


0 commentaires

2
votes

Une simple fonction récursive de retour en arrière les visitera dans l'ordre du premier exemple avec quelque chose comme:

function* iter(maxlength, prefix = []){
  if (prefix.length >= maxlength) return
   for (let i = 0; i < 2; i++){
     yield [i, ...prefix]
     yield * iter(maxlength, [i, ...prefix])
   }
}
  
console.log([...iter(3)].map(a => a.join(',')))

Vous pouvez également générer un tableau avec la même idée et une fonction génératrice (ici, il renvoie un tableau de tableaux à joindre plus tard, mais le même principe):

function iter(maxlength, cur = ''){
  if (cur.length >= maxlength) return
  for (let i = 0; i < 2; i++){
    console.log(cur.length + 1 + ":",   cur + i)
    iter(maxlength, cur + i)
  }
}

iter(3)


1 commentaires

Vous réduisez le problème-échantillon en un algorithme de chaîne pure, mais il est très élégant (!), Il en est de même d'une réponse valable ... Eh bien, comment voter "accepté"? C'est peut-être pour les lecteurs le plus simple et le plus didactique, mais la solution de Nina est la plus utile pour moi.



2
votes

Je pense que vous recherchez

callManyTimes([2,2,2], show);

function callManyTimes(maxIndices, func, args=[]) {
    if (maxIndices.length == 0) {
        func(...args);
    } else {
        func(...args);
        var rest = maxIndices.slice(1);
        var index = args.length;
        args = args.slice();
        for (args[index] = 0; args[index] < maxIndices[0]; ++args[index]) {
            callManyTimes(rest, func, args);
        }
    }
}

function show(...args) {
   let code = args.join(" ");
   console.log(args.length + ": "+ code )
}

Au lieu de cette boucle while , vous voulez appeler func une seule fois. Pour obtenir les résultats partiels, vous placez un appel func avant la boucle avec les appels récursifs, comme vous l'avez fait dans votre version étendue. J'ai également éliminé le paramètre index qui est juste le args.length , et j'ai fait des copies de args avant d'ajouter un nouveau niveau.

Vous devez également simplement utiliser la syntaxe de propagation pour l'appel, car vous recevez les arguments dans show avec la syntaxe de paramètre rest.


2 commentaires

Merci (!), Une correction parfaite de mon code "essayer de résoudre".


Oui, il vaut mieux répondre que votre question précédente :-)



0
votes

Vous pouvez écrire la sortie à la main et utiliser .repeat()

const f = (a,b,c=[a+b,b+a,a+b+a,a+a+b,a+b+b,b+a+b,b+b+a,b+a+a]) => {
  for(let i=3;i;c.push(a.repeat(i),b.repeat(i)),i--);return c
};

console.log(f('0','1'));


2 commentaires

Bon programme one-liner (!), C'est comme un cache combinatoire ... Mais ce n'est pas général, il faut aussi un générateur combinatoire pour être général (il ne s'agit pas seulement de changer 3 en 8 pour obtenir la boucle à 8 niveaux).


@PeterKrauss Oui, la partie du code a été composée à la main en raison de la sortie restreinte. Similaire au fait d'avoir généralement moins d'octets à écrire let a = 'abcdefghijklmnopqrstuvwxyz que a = `$ {Object.keys (top)}` .match (/ [az] / g) (non trié) ou a = [... new Set (`$ {Object.keys (top)}` .match (/ [az] / g) .sort ())]. join