La récursivité
Introduction
En programmation, le concept de récursivité désigne une fonction qui s’appelle elle-même.
Pour bien comprendre, imaginons une fonction qui explore des dossiers sur un ordinateur.
Chaque dossier peut contenir :
- des fichiers simples 📄 ;
- d’autres dossiers 📁 à explorer à leur tour.
Plutôt que d’écrire une fonction compliquée qui gère tous les niveaux à la fois, on peut écrire une fonction qui fait toujours la même chose :
“Parcours le contenu du dossier courant, et si tu trouves un sous-dossier, appelle-toi pour explorer ce sous-dossier.”
Pile d’exécution : comprendre ce qu’il se passe
Pour exécuter du code, Python utilise une pile d’appels (ou pile d’exécution).
À chaque fois qu’une fonction est appelée :
- Python la place sur le dessus de la pile,
- exécute son contenu,
- puis la retire (dépile) une fois terminée.
Imaginons deux fonctions :
def fctA():
print("Début de A")
print("Fin de A")
def fctB():
print("Début de B")
fctA()
print("Fin de B")
fctB()
Voici ce qu’il se passe :
fctB()est appelée → elle est mise sur la pile.- À l’intérieur,
fctA()est appelée → on l'empile au-dessus. fctA()s’exécute entièrement puis se dépile.- On reprend
fctB()là où on s’était arrêté.
En récursivité, le même schéma s'applique : la fonction appelée est empiliée sur la pile d'exécution, et si elle s'appelle elle-même, alors cette même fonction s'empilera, et le programme s'arrêtera lorsque toutes les fonctions auront été exécutées et dépilées.
Définition de la récursivité
Une fonction récursive est une fonction qui s’appelle elle-même.
def explore(dossier):
for element in dossier:
if element.est_un_fichier():
print(element.nom)
else:
explore(element) # appel récursif !
Ici, la fonction explore() s’appelle à nouveau pour chaque sous-dossier.
Chaque appel crée une nouvelle instance de la fonction dans la pile d’exécution.
Les deux cas d’une fonction récursive
Une fonction récursive doit toujours distinguer deux cas :
- Le cas d’arrêt : celui où la fonction ne doit plus s’appeler elle-même.
- Le cas général : celui où la fonction continue de s’appeler.
Imaginons qu’on veuille afficher un compte à rebours :
def compte_a_rebours(n):
if n == 0: # ✅ cas d’arrêt
print("Décollage !")
else: # 🔁 cas général
print(n)
compte_a_rebours(n - 1)
👉 Ici :
- Cas général : on affiche
npuis on s’appelle avecn-1. - Cas d’arrêt : quand
nvaut0, on s’arrête.
Comment raisonner face à un problème récursif
- Identifier le cas d’arrêt (quand doit-on s’arrêter ?).
- Identifier le cas général (quelle action se répète ?).
- Vérifier que chaque appel rapproche du cas d’arrêt.
Une erreur classique est d’oublier le cas d’arrêt : dans ce cas, la fonction s’appelle à l’infini jusqu’à provoquer un dépassement de pile (RecursionError en Python).
Exemple mathématique : somme des n premiers entiers
Formule connue :
- S(n) = n + S(n-1) avec
- S(0) = 0
Traduit en Python :
def somme(n):
if n == 0: # cas d'arrêt
return 0
else: # cas général
return n + somme(n - 1)
Appelons somme(3) :
somme(3)
→ 3 + somme(2)
→ 3 + (2 + somme(1))
→ 3 + (2 + (1 + somme(0)))
→ 3 + (2 + (1 + 0))
= 6
Chaque appel est mis sur la pile, puis “se résout” quand on revient en arrière :
somme(3)
└── somme(2)
└── somme(1)
└── somme(0)
À retenir
- Une fonction récursive s’appelle elle-même.
- Il faut toujours définir :
- un cas d’arrêt (condition de fin),
- un cas général (ce qui se répète).
- Chaque appel est placé sur la pile d’exécution, puis dépilé à la fin.
- La récursivité permet d’écrire des fonctions élégantes pour des problèmes répétitifs (exploration de dossiers, structures arborescentes, suites mathématiques, etc.).