web-dev-qa-db-fra.com

Exemples de fonctions récursives

Quelqu'un peut-il suggérer des exemples de programmation qui illustrent des fonctions récursives? Il y a les vieux chevaux habituels tels que Série Fibonacci et Tours de Hanoi , mais tout le reste serait amusant.

34
Codeslayer

Cette illustration est en anglais, plutôt qu'un langage de programmation réel, mais est utile pour expliquer le processus de manière non technique:

 Un enfant ne pouvait pas dormir, alors sa mère a raconté une histoire à propos d'une petite grenouille, 
 Qui ne pouvait pas dormir, alors la mère de la grenouille a raconté une histoire à propos d'un petit ours, 
 qui ne pouvait pas dormir, alors la mère de l'ours a raconté l'histoire d'une petite belette 
 ... qui s'est endormie. 
 ... et le petit ours s'est endormi; 
. ..et la petite grenouille s'est endormie; 
 ... et l'enfant s'est endormi. 
119
ConroyP

Pour comprendrerécursivité , il faut d'abord comprendre récursivité .

33
Ilya Ryzhenkov

La règle générale pour la récursivité est: "Utilisez la récursivité, si et seulement si à chaque itération votre tâche se divise en deux ou plus tâches similaires".

Fibonacci n'est donc pas un bon exemple d'application de récursivité, tandis que Hanoi en est un bon.

Ainsi, la plupart des bons exemples de récursivité sont la traversée des arbres dans différentes disparités.

Par exemple: traversée de graphe - l'exigence selon laquelle le nœud visité ne sera plus jamais visité fait du graphe un arbre (un arbre est un graphe connecté sans cycles simples)

algorithmes de division et de conquête (tri rapide, tri par fusion) - les parties après "diviser" constituent des nœuds enfants, "conquérir" constitue les bords du nœud parent aux nœuds enfants.

18
Kaerber

Que diriez-vous de tester une chaîne pour être un palindrome?

bool isPalindrome(char* s, int len)
{
  if(len < 2)
    return TRUE;
  else
    return s[0] == s[len-1] && isPalindrome(&s[1], len-2);
}

Bien sûr, vous pouvez le faire avec une boucle plus efficacement.

15
Kip
13
Martin Cote

Un autre couple de "suspects habituels" sont Quicksort et MergeSort

10
agnul

Du monde des mathématiques, il y a la fonction Ackermann :

Ackermann(m, n)
{
  if(m==0)
    return n+1;
  else if(m>0 && n==0)
    return Ackermann(m-1, 1);
  else if(m>0 && n>0)
    return Ackermann(m-1, Ackermann(m, n-1));
  else
    throw exception; //not defined for negative m or n
}

Il se termine toujours, mais il produit des résultats extrêmement importants même pour de très petites entrées. Ackermann (4, 2), par exemple, renvoie 265536 - 3.

9
Kip

Voici un exemple pragmatique du monde des systèmes de fichiers. Cet utilitaire compte récursivement les fichiers dans un répertoire spécifié. (Je ne me souviens pas pourquoi, mais j'avais en fait besoin de quelque chose comme ça il y a longtemps ...)

public static int countFiles(File f) {
    if (f.isFile()){
        return 1;
    }

    // Count children & recurse into subdirs:
    int count = 0;
    File[] files = f.listFiles();
    for (File fileOrDir : files) {
        count += countFiles(fileOrDir);
    }
    return count;
}

(Notez que dans Java une instance File peut représenter soit un fichier normal soit un répertoire. Cet utilitaire exclut les répertoires du décompte.)

Un exemple courant du monde réel serait par exemple FileUtils.deleteDirectory() de la bibliothèque Commons IO ; voir le API doc & source .

6
Jonik

À mon avis, la récursivité est bonne à savoir, mais la plupart des solutions qui pourraient utiliser la récursivité pourraient également être faites en utilisant l'itération, et l'itération est de loin plus efficace.

Cela dit, voici une façon récursive de trouver un contrôle dans une arborescence imbriquée (comme ASP.NET ou Winforms):

public Control FindControl(Control startControl, string id)
{
      if (startControl.Id == id)
           return startControl

      if (startControl.Children.Count > 0)
      {
           foreach (Control c in startControl.Children)
           {
                return FindControl(c, id);
           }
      }
      return null;
}
6
FlySwat

Le modèle de conception de l'interpréteur est un très bel exemple car beaucoup de gens ne repèrent pas la récursivité. L'exemple de code répertorié dans l'article Wikipedia illustre bien comment cela peut être appliqué. Cependant, une approche beaucoup plus basique qui implémente toujours le modèle d'interpréteur est une fonction ToString pour les listes imbriquées:

class List {
    public List(params object[] items) {
        foreach (object o in items)
            this.Add(o);
    }

    // Most of the implementation omitted …
    public override string ToString() {
        var ret = new StringBuilder();
        ret.Append("( ");
        foreach (object o in this) {
            ret.Append(o);
            ret.Append(" ");
        }
        ret.Append(")");
        return ret.ToString();
    }
}

var lst = new List(1, 2, new List(3, 4), new List(new List(5), 6), 7);
Console.WriteLine(lst);
// yields:
// ( 1 2 ( 3 4 ) ( ( 5 ) 6 ) 7 )

(Oui, je sais qu'il n'est pas facile de repérer le modèle d'interpréteur dans le code ci-dessus si vous vous attendez à une fonction appelée Eval… mais vraiment, le modèle d'interprète ne nous dit pas comment la fonction est appelée ou même ce qu'elle ne et le livre du GoF énumère explicitement ce qui précède comme exemple dudit modèle.)

6
Konrad Rudolph

Un exemple concret est le problème du "coût de la nomenclature".

Supposons que nous ayons une entreprise de fabrication qui fabrique des produits finaux. Chaque produit peut être décrit par une liste de ses pièces et le temps requis pour assembler ces pièces. Par exemple, nous fabriquons des perceuses électriques à main à partir d'un boîtier, d'un moteur, d'un mandrin, d'un interrupteur et d'un cordon, et cela prend 5 minutes.

Compte tenu d'un coût de main-d'œuvre standard par minute, combien cela coûte-t-il de fabriquer chacun de nos produits?

Oh, au fait, certaines pièces (par exemple le cordon) sont achetées, nous connaissons donc directement leur coût.

Mais nous fabriquons nous-mêmes certaines pièces. Nous fabriquons un moteur à partir d'un boîtier, d'un stator, d'un rotor, d'un arbre et de roulements, et cela prend 15 minutes.

Et nous fabriquons le stator et le rotor à partir d'estampages et de fils, ...

Ainsi, déterminer le coût d'un produit fini revient en fait à parcourir l'arborescence qui représente toutes les relations ensemble-à-liste-de-pièces dans nos processus. Cela s'exprime bien avec un algorithme récursif. Cela peut certainement être fait de manière itérative également, mais l'idée de base se mélange avec la comptabilité à faire soi-même, donc ce n'est pas aussi clair ce qui se passe.

5
joel.neely

Comme d'autres l'ont déjà dit, beaucoup d'exemples canoniques de récursivité sont académiques.

Certaines utilisations pratiques que j'ai rencontrées dans le passé sont:

1 - Navigation dans une arborescence, comme un système de fichiers ou le registre

2 - Manipulation des contrôles de conteneur qui peuvent contenir d'autres contrôles de conteneur (comme les panneaux ou les GroupBox)

2
JosephStyons

L'exemple le plus poilu que je connaisse est celui de Knuth test homme ou garçon . En plus de la récursivité, il utilise les fonctionnalités ALGOL des définitions de fonction imbriquées (fermetures), des références de fonction et du dualisme constante/fonction (appel par nom).

2
Hugh Allen

Mon préféré est Recherche binaire

Edit: Aussi, traversée d'arbre. En parcourant une structure de fichiers de dossier par exemple.

1
Geoff

Implémentation des graphiques par Guido van Rossum a quelques fonctions récursives dans Python pour trouver des chemins entre deux nœuds dans les graphiques.

1
sanxiyn

Mon tri préféré, Tri par fusion

(Favori car je me souviens de l'algorithme et n'est-ce pas trop mauvais en termes de performances)

1
crashmstr
  • Factorielle
  • Traverser un arbre en profondeur (dans un système de fichiers, un espace de jeu ou tout autre cas)
0
Vinko Vrsalovic

Il était une fois, et il n'y a pas si longtemps, des écoliers du primaire ont appris la récursivité en utilisant Logo et Turtle Graphics. http://en.wikipedia.org/wiki/Turtle_graphics

La récursivité est également idéale pour résoudre des énigmes par un essai exhaustif. Il y a une sorte de puzzle appelé "remplir" (Google it) dans lequel vous obtenez une grille comme un mot croisé, et les mots, mais pas d'indices, pas de carrés numérotés. J'ai écrit une fois un programme utilisant la récursivité pour un éditeur de puzzles pour résoudre les puzzles afin d'être sûr que la solution connue était unique.

0
SeaDrive

Que diriez-vous d'inverser une chaîne?

void rev(string s) {
  if (!s.empty()) {
    rev(s[1..s.length]);
  }
  print(s[0]);
}

Comprendre cela aide à comprendre la récursivité.

0
Yuval F

Voici un exemple que j'ai publié sur ce site il y a quelque temps pour générer récursivement une arborescence de menus: Exemple récursif

0
JPrescottSanders

Que diriez-vous de quelque chose listes de traitement , comme:

  • carte (et andmap, ormap)
  • plier (foldl, foldr)
  • filtre
  • etc...
0
pkaeding

Les fonctions récursives sont idéales pour travailler avec types de données définis de manière récursive :

  • Un nombre naturel est zéro ou le successeur d'un autre nombre naturel
  • Une liste est la liste vide ou une autre liste avec un élément devant
  • Un arbre est un nœud avec des données et zéro ou plusieurs autres sous-arbres

Etc.

0
Bruno De Fraine