web-dev-qa-db-fra.com

L'élément Spread transforme magiquement les fonctions en 'non fonctions'

Supposons que j'ai cette simple fonction JavaScript:

function returnArray(){
    return [1, 2, 3];
}

Supposons en outre que je dise alors

var test = [0, ...returnArray()];

Vous vous attendriez à ce que le test soit égal à [0,1,2,3] et vous auriez raison. J'ai essayé ça et bien sûr ça marche. 

Maintenant, j'ai cet exercice où je suis censé construire une fonction appelée double qui prend un tableau en paramètre et retourne un autre tableau qui contient toutes les valeurs doublées du tableau d'origine. Donc si j'appelle double ([1,2,3]) je devrais obtenir [2,4,6]. Les contraintes de l'exercice sont que je dois utiliser seulement tableau destructurant, récursif et les opérateurs rest/spread pour créer ma fonction. Aucun assistant de tableau n'est autorisé. Alors je suis venu avec ceci:

function double(array){
  if (array.length===1) return 2*array[0];
  var [num, ...rest] = array;  
  if (rest.length!=0) return [2*num, ...double(rest)];  
}

Si j'exécute cette fonction avec un tableau dont la taille est au moins deux, je reçois un message d'erreur indiquant que double n'est pas une fonction. Si je supprime l'opérateur ... avant double, comme par magie, double est à nouveau une fonction, sauf que, bien entendu, le résultat de double ([1,2,3]) est [2, [4,6]], ce qui n'est pas tout à fait le même que [2,4,6]. 

Ma première pensée a été que peut-être, pour une raison étrange, vous ne pouvez pas utiliser ... devant une fonction, même si la fonction retourne un tableau, j'ai donc testé cette hypothèse avec ma fonction returnArray () ci-dessus, et j'ai trouvé que cela fonctionne très bien. Je ne sais pas pourquoi cela tombe en panne dans l'exercice. Je ne peux que supposer que la récursivité y est pour quelque chose, mais je ne vois pas pourquoi. Quelqu'un peut-il signaler ce qui ne va pas mon code?

EDIT: Merci à tous, ce fut une erreur stupide! J'aurais dû le voir. J'ai ajouté des commentaires à certaines de vos réponses.

15
Nicola

C'est un message d'erreur très étrange, pas de question, mais le problème principal est une erreur logique dans double: dans deux branches du code, l'appel de double donne une valeur non-itérable (dans un cas un nombre, dans un autre undefined). Mais vous appliquez toujours la notation par propagation. Donc, cela échoue dans ces deux cas. Les affaires sont les suivantes:

  1. Vous renvoyez uniquement le nombre, pas un tableau, dans le cas array.length === 1.
  2. Vous ne renvoyez rien dans le cas où array.length n'est pas 1 et rest.length est 0, le résultat de l'appel de double dans ce cas est donc undefined.

Vous frappez le cas où vous essayez de répartir un nombre en premier, comme ceci:

function a() {
  return 42;
}
const b = [...a()];

Pour # 1, vous devriez retourner un tableau avec une entrée. Pour # 2, vous devriez renvoyer []. Donc, la version de modifications minimales est:

function double(array) {
  if (array.length === 1) {
    return [2*array[0]];
    //     ^−−−−−−−−−−^−−−−−−−−−−− note
  }
  var [num, ...rest] = array;  
  if (rest.length > 0) {
    return [2*num, ...double(rest)];
  }
  return []; // <−−−−−−−−−−−−−−−−− note
}
console.log(double([1,2,3]));

10
T.J. Crowder

Votre erreur logique a été signalée dans les commentaires et les réponses, mais laissez-moi vous indiquer une manière plus propre, plus simple et moins sujette aux bogues, d’écrire celle-ci qui est plus conforme aux principes de base de la récursivité.

function double([head, ...tail]) {
  if (head === undefined) return [];
  return [2*head, ...double(tail)];
}

En d'autres termes, il n'y a qu'un seul "cas de base", à savoir le tableau vide, qui renvoie un tableau vide. Tout le reste est simple récursivité. 

Vous pouvez "fonctionnaliser" cet autre avec

function map(fn) {
  return function iter([head, ...tail]) {
    return head === undefined ? [] : [fn(head), ...iter(tail)];
  };
}

const double = map(x => 2*x);
console.log(double([1, 2, 3]));
24
user663031

Juste un changement mineur:

function double(array) {
  // note the return here is an array, not a number
  if (array.length === 1) return [2 * array[0]];

  var [num, ...rest] = array;
  if (rest.length) return [2 * num, ...double(rest)];
}

console.log(double([1, 2, 3, 4]));

Vous retourniez un numéro et la déstructuration d'un numéro vous laissera une erreur.

...5 // throws SyntaxError
3
chazsolo

Voici l'erreur que j'obtiens en utilisant [email protected]

if (rest.length!=0) return [2*num, ...double(rest)];  
                                        ^

TypeError: double(...)[Symbol.iterator] is not a function
    at double (/home/henrique/labs/test.js:4:41)
    at double (/home/henrique/labs/test.js:4:41)
    at Object.<anonymous> (/home/henrique/labs/test.js:7:1)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:390:7)

Ce qui signifie probablement que le résultat de l'évaluation d'une partie de votre appel de fonction n'est pas itérable.

L'erreur est ici:

if (array.length===1) return 2*array[0];

Changer en:

if (array.length===1) return [2*array[0]];

et ça va marcher.

1