IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Compréhension fonction récursive


Sujet :

JavaScript

  1. #1
    Membre confirmé Avatar de arwin
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 59
    Par défaut Compréhension fonction récursive
    Bonjour,
    voici une fonction récursive qui rempli un tableau avec des chiffres allant de 1 à n.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
     
    function countup(n) {
      if (n < 1) {
        return [];
      } else {
        const countArray = countup(n - 1); 
        countArray.push(n);
        return countArray;
      }
    }
    console.log(countup(5));
    Je bloque dans la compréhension du fonctionnement de cette fonction. La ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const countArray = countup(n - 1);
    est censée appeler la fonction elle même avec comme paramètre n-1 donc on va au début de la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function countup(n-1) {
      if (n-1 < 1) {
        return [];
      } else
    et on va tomber encore une fois sur la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const countArray = countup(n - 2);
    qui nous fait remonter encore au début de la fonction et ainsi de suite jusqu'à n = 0 pour lequel la fonction retourne un tableau vide (et ne dépasse pas le else).

    Du coup on ne pourra jamais atteindre la ligne d'après, à savoir et donc on ne pourra rien "pusher" dans le tableau .... Parce que, et si mes connaissances sont correctes, lorsqu'on appelle une fonction on ne va plus exécuter le code qui se trouve après jusqu'à l'exécution complète de la fonction ....

    J'ai l'impression d'avoir raté quelque chose de fondamental ? c'est grave docteur ?

  2. #2
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Bonjour,
    pour t'aider à comprendre ce qui ce passe, pas toujours évident d'ailleurs, il faut que tu observes le cheminement et rien de tel que de mettre des console.log().

    Essaie avec ce code et tu vas te rendre compte que tu as occulté quelque chose quand tu écris « Du coup on ne pourra jamais atteindre la ligne d'après, à savoir  ».
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function countup(n) {
      if (n < 1) {
        console.log("end")
        return [];                           // création de l'array à remplir
      } else {
        console.log("else :", n)
        const countArray = countup(n - 1);   // ici on appelles la fonction avant d'exécuter ce qui suit 
        countArray.push(n);
        console.log(JSON.stringify(countArray));
        return countArray;
      }
    }
    console.log("Result : ", countup(5));

  3. #3
    Membre confirmé Avatar de arwin
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 59
    Par défaut
    Merci NoSmoking.. ton idée est génial mais je n'arrive toujours pas à saisir le truc.
    Après la ligne "end" le programme va retourner un tableau vide ... jusqu'à là c'est clair. Mais après il devrait sauter le else puisqu'on est dans le cas n=0 et donc on devrait sortir de la fonction et arriver à la ligne : console.log("Result : ", countup(5)); non ?
    Je ne comprend pas comment on a obtenu la ligne : [1] et ce qui suit
    Images attachées Images attachées  

  4. #4
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    Mais après il devrait sauter le else puisqu'on est dans le cas n=0 et donc on devrait sortir de la fonction
    tu oublies que la fonction s'auto appelle donc il y a un empilement puis un dépilement des appels.

    Essaies de comprendre en regardant ce pseudo code, ne cherche pas à l'exécuter, il plantera, c'est juste pour visualiser les empilements dépilements
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    function countup(n)
      { // ce bloc est exécuté
        // ici n = 5
        if (n < 1) { // non exécutée
          console.log("end")
          return [];
        }
        else {
          const countArray = countup(n - 1)
          { // ce bloc est exécuté
            // ici n = 4
            if (n < 1) { // non exécutée
              return [];
            }
            else {
              const countArray = countup(n - 1)
              { // ce bloc est exécuté
                // ici n = 3
                if (n < 1) { // non exécutée
                  return [];
                }
                else {
                  const countArray = countup(n - 1)
                  { // ce bloc est exécuté
                    // ici n = 2
                    if (n < 1) { // non exécutée
                      return [];
                    }
                    else {
                      const countArray = countup(n - 1)
                      { // ce bloc est exécuté
                        // ici n = 1
                        if (n < 1) { // non exécutée
                          return [];
                        }
                        else {
                          const countArray = countup(n - 1)
                          { // ce bloc est exécuté
                            // ici n = 0, on arrête tout
                            if (n < 1) {
                              return [];
                            }
                            else { // non exécutée
                              const countArray = countup(n - 1);
                              countArray.push(n);
                              return countArray;
                            }
                          };
                          // maintenant countArray = []
                          // l'exécution se poursuit avec n = 1
                          countArray.push(n);
                          // countArray = [1]
                          return countArray;
                        }
                      };
                      // l'exécution se poursuit avec n = 2
                      countArray.push(n);
                      // countArray = [1, 2]
                      return countArray;
                    }
                  };
                  // l'exécution se poursuit avec n = 3
                  countArray.push(n);
                  // countArray = [1, 2, 3]
                  return countArray;
                }
              }
              // l'exécution se poursuit avec n = 4
              countArray.push(n);
              // countArray = [1, 2, 3, 4]
              return countArray;
            }
          };
          // l'exécution se poursuit avec n = 5
          countArray.push(n);
          // countArray = [1, 2, 3, 4, 5]
          return countArray;
        }
      }
    // l'exécution est terminée
    console.log("Result : ", countup(5));

  5. #5
    Membre confirmé Avatar de arwin
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 59
    Par défaut
    c'est bien de décortiquer le code, ca permet de voir où ça coince précisément et là du coup c'est à la ligne 50 : // l'exécution se poursuit avec n = 1 !!!!
    comment et pourquoi ? on était à n = 0 non ?! ( ligne 39 // ici n = 0, on arrête tout). Est-ce que le cas n = 1 (2, 3 etc ..) a été gardé en mémoire quelque part ?

    Parce que la limite qui reste floue dans ma tête c'est lorsqu'on arrive à n = 1,
    on a alors : const countArray = countup(0);
    On remonte alors au début de la fonction avant même de stocker quoi que ce soit dans countArray .... donc vraiment je ne vois pas comment ça se produit ce dépilement dont tu parle NoSmoking !!!!

  6. #6
    Expert confirmé
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 652
    Par défaut
    à la ligne 37 du code NoSmoking, il y a l'appel de "countup(n - 1)" avec n = 1.
    donc à la ligne 50, on est dans même contexte donc il s'agit d'une variable n qui contient encore 1.

    chaque appel de la fonction countup va créer une nouvelle variable n qui gardera sa valeur uniquement pendant l'exécution de cette fonction. et donc la valeur de cette variable est indépendante des autres variables n des autres appels même si c'est la même fonction.

  7. #7
    Membre confirmé Avatar de arwin
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 59
    Par défaut
    Merci Mathieu ... je pense que tu n'as pas vu ce qu'ai rajouté à mon message ... pour le cas n=1 :

    "
    on a alors : const countArray = countup(0);
    On remonte alors au début de la fonction avant même de stocker quoi que ce soit dans countArray
    "
    Donc il y a bien l'appel de la fonction countup(0) avant de pouvoir éventuellement exécuter le reste du code ?

    Il y a vraiment quelque chose qui m'échappe là ...

    et lorsque tu dis "chaque appel de la fonction countup va créer une nouvelle variable n qui gardera sa valeur uniquement pendant l'exécution de cette fonction. et donc la valeur de cette variable est indépendante des autres variables n des autres appels ..." .... pourquoi on commence par 1 puis 2 etc .... je ne vois pas bien cette histoire de pilement et de dépilement ? ça se passe où ? dans quel ordre ? à quel niveau ?

  8. #8
    Expert confirmé
    Avatar de mathieu
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    10 652
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 10 652
    Par défaut
    cet empilement se fait dans le moteur javascript, c'est comme si le code lancé était le suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    console.log(countup_appelN1(1));
     
    function countup_appelN1(n_appelN1) {
      if (n_appelN1 < 1) {
        return [];
      } else {
        const countArray_appelN1 = countup_appelN0(n_appelN1 - 1); 
        countArray_appelN1.push(n_appelN1);
        return countArray_appelN1;
      }
    }
     
    function countup_appelN0(n_appelN0) {
      if (n_appelN0 < 1) {
        return [];
      } else {
        const countArray_appelN0 = countup...(n_appelN0 - 1); 
        countArray_appelN0.push(n_appelN0);
        return countArray_appelN0;
      }
    }

  9. #9
    Membre confirmé Avatar de arwin
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 59
    Par défaut
    Donc, si je comprend bien, à chaque fois qu'on appelle countup(5); countup(4); countup(3); etc .. le moteur JS la stocke quelque part ... (stocke quoi en fait ? la valeur ? mais la valeur ne peut pas être calculée à ce stade !!! l'expression ?!!) et qu'une fois qu'on on arrive au 'base case' (dans notre cas n = 0), ce même moteur procède au dépilment en commençant par le dernier appel (chronologiquement parlant) de la fonction (c'est-à-dire countup(1)), puis countup(2) etc ... en la stockant dans le countArray et en procedant au push par la suite; ???

  10. #10
    Modérateur

    Avatar de NoSmoking
    Homme Profil pro
    Inscrit en
    Janvier 2011
    Messages
    17 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 17 198
    Par défaut
    le moteur JS la stocke quelque part
    Contexte d'exécution et pile d'exécution, ou autres termes, voilà des mots qu'il te faut approcher si tu veux aller plus loin dans la compréhension.

    Un petit schéma pour te montrer comment est exécutée ta récursivité
    Nom : context-js.png
Affichages : 147
Taille : 4,1 Ko

    Tu trouveras plus d'info sur : Récursion et pile (en français)

  11. #11
    Expert confirmé
    Avatar de javatwister
    Homme Profil pro
    danseur
    Inscrit en
    Août 2003
    Messages
    3 684
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : danseur

    Informations forums :
    Inscription : Août 2003
    Messages : 3 684
    Par défaut
    C'est sûr que c'est moins intuitif que ça

    Code javascript : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    function countup(n) {
    	n.unshift(n[0]-1);
    	return n[0]>1 ? countup(n) : n
    }
    console.log(countup([5]));

    mais ça remue bien les neurones

  12. #12
    Membre confirmé Avatar de arwin
    Profil pro
    Inscrit en
    Mars 2009
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2009
    Messages : 59
    Par défaut
    Merci les gars ... ....

    c'était pas évident mais les choses sont beaucoup plus claires maintenant.

    Je poste un petit dessin qui pourrait aider d'autres personnes à comprendre plus facilement / plus rapidement le principe de la récursivité.

    Super ton code javatwister ... et effectivement ca apprendra aux plus paresseux des neurones à danser et la java et le twist en même temps
    Images attachées Images attachées  

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 19/04/2019, 11h10
  2. [VB6] XML, fonction récursive de recherche
    Par kboo dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 24/04/2006, 21h27
  3. [XSLT] fonction récursive à N niveaux
    Par Mike35 dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 10/03/2006, 12h30
  4. Fonction récursive renvoi sur page d'erreur
    Par peck dans le forum Langage
    Réponses: 1
    Dernier message: 23/12/2005, 10h08
  5. Problème de fonction récursive avec un TcxDBTreeList
    Par isachat666 dans le forum Composants VCL
    Réponses: 1
    Dernier message: 05/12/2005, 13h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo