Les boucles récursives : Ou comment une procédure se lance elle-même ! [#18 FORMATION EXCEL VBA COMPLETE]
Au cours des chapitre précédent de la formation sur l’apprentissage de la création des macros VBA, nous avons découvert comment créer des boucles pour répéter une portion de code. Nous y avons vu des types de boucles classiques (For, Whil et Until) sous différentes déclinaisons. Dans ce nouveau chapitre nous allons découvrir un nouveau type de boucle, beaucoup moins conventionnel ! Il s’agit de la boucle récursive !
Téléchargement
Vous pouvez télécharger le fichier d'exemple de cet article en cliquant sur le lien suivant :
Tutoriel Vidéo
Vous trouverez très régulièrement de nouvelles vidéos sur la chaîne, alors pensez à vous abonner pour ne manquer aucune astuce et devenir rapidement un pro d’Excel (cliquez-ici) !
1. Présentation
Le principe de la récursivité appliqué au développement bien souvent délaissé par les développeurs, alors que celui-ci peut parfois être d’une aide très précieuse : en effet, une procédure est dite récursive lorsque celle-ci s’appelle elle-même !
Cette notion simple à comprendre est toutefois à l’opposé d’un principe fort d’Excel qui est qu’une formule ne peut pas appeler utiliser son propre résultat (ou celui d’une autre cellule dont le résultat découle directement d’elle-même) sous peine de générer ce qu’Excel appelle une « référence circulaire » ! :
En réalité, et même si ce n’est pas le sujet de cette vidéo, sachez qu’il possible de réaliser ce type de calcul en activant les calculs itératifs dans Excel, ce permet de mettre en place des formules qui suivent les principes que nous voyons dans ce chapitre, directement au sein des feuilles de calculs.
2. Exemple du Quizz
Si le principe de récursivité que nous venons juste d’énoncer peut encore paraître assez flou, voyons un exemple pour illustrer son fonctionnement.
Pour cela, nous allons reprendre l’exemple du quizz que nous avons développé dans le chapitre précédent.
Voici donc le code départ :
Sub quizzAdditionRecursif()
Dim a As Integer, b As Integer
a = 4
b = 2
Do Until InputBox("Combien font " & a & " + " & b & "?") = a + b
Do
Randomize
a = CInt(Rnd * 10)
b = CInt(Rnd * 10)
Loop Until a = b
Loop
MsgBox "Bravo, vous avez gagné !"
End Sub
Nous ne souhaitons plus utiliser de boucle classique du type Do Until, mais une récursivité de la procédure quizzAdditionRecursif.
Pour cela, nous effectuerons juste un test classique (If… Then… Else…) pour vérifier la réponse donnée par l’utilisateur, et en fonction de son résultat :
- Si le résultat est erroné, c’est-à-dire que la réponse donnée ne correspond pas à la somme des variables a et b, alors nous appelons à nouveau la procédure,
- En revanche, si le résultat est valide, alors nous affichons simplement le message de félicitation, et la procédure se termine
Sub quizzAdditionRecursif()
Dim a As Integer, b As Integer
Randomize
a = CInt(Rnd * 10)
b = CInt(Rnd * 10)
If InputBox("Combien font " & a & " + " & b & "?") <> a + b Then
quizzAdditionRecursif
Else
MsgBox "Bravo, vous avez gagné !"
End If
End Sub
3. Exemple du calcul de factorielle
Un autre exemple d’utilisation d’une boucle récursive et le calcul de la factorielle d’un nombre.
Si les cours de mathématiques vous paraissent assez lointain, sachez que la factorielle d’un nombre n, notée n! correspond au produit de l’ensemble des nombres compris entre 1 et ce nombre n.
Ainsi, la factorielle de 5, notée 5! est égale à 120 :
5! = 1*2*3*4*5
5! = 120
Pour créer une fonction qui permette d’obtenir ce résultat, nous pourrions classiquement utiliser une boucle For :
Function factorielle(n As Integer) As Integer
Dim i As Integer, r As Integer
r = 1
For i = 1 To n
r = r * i
Next
factorielle = r
End Function
Mais nous pourrions également utiliser une boucle récursive :
Function factorielleR(n As Integer) As Integer
If n < 1 Then
factorielleR = 1
Else
factorielleR = n * factorielleR(n - 1)
End If
End Function
Cette méthode est légèrement moins intuitive que la simple utilisation d’une boucle For, en effet le résultat de la fonction factorielle dépend directement du résultat de celle-ci.
Pour mieux appréhender la méthode de résolution, décomposons chacune des étapes du calcul de la factorielle de 5.
- Étape 1 : pour cette première étape, nous déclarons que n est égal à 5. Le test n < 1 renvoie redirige ainsi le code sur la partie située après le Else, laquelle défini la valeur de retour comme étant égal à la valeur de n (donc 5) à laquelle s’ajoute la valeur de la factorielle de n-1 (donc 5-1=4),
- Étape 2 : le code procède exactement de la même manière pour déterminer le résultat de la factorielle de 4 (qui dépend lui-même du résultat de la factorielle de 3),
- Étape 3 : puis le résultat de la factorielle de 3 (qui dépend lui-même du résultat de la factorielle de 2),
- Étape 4 : puis le résultat de la factorielle de 2 (qui dépend lui-même du résultat de la factorielle de 1),
- Étape 5 : puis au moment de déterminer le résultat de la factorielle de 1, le test If nous indique que celui-ci est égal à 1 !
- Étape 6 : ne reste ensuite plus qu’à remonter les résultats dans chacune des fonctions récursives pour obtenir un résultat de 120 !
factorielle(5) = 5 * factorielle(4)
factorielle(5) = 5 * (4 * factorielle(3))
factorielle(5) = 5 * (4 * (3 * factorielle(2)))
factorielle(5) = 5 * (4 * (3 * (2 * factorielle(1))))
factorielle(5) = 5 * (4 * (3 * (2 * 1)))
factorielle(5) = 120
4. Faut-il utiliser les boucles récursives ?
Pour déterminer les durées d’exécution des deux méthodes que nous venons de voir, nous allons utiliser la méthode de chronométrage que nous avions déjà mis en place au cours d’une des chapitres précédents :
Sub testFactorielle()
Dim heureDebut As Single, i As Long
' Méthode classique : For
heureDebut = Timer
For i = 1 To 1000000
factorielle (10)
Next
Debug.Print "Boucle For : " & Timer - heureDebut
' Méthode récursive
heureDebut = Timer
For i = 1 To 1000000
factorielleR (10)
Next
Debug.Print "Boucle Récursive : " & Timer - heureDebut
End Sub
Ici, nous calculons un million de fois la factorielle de 5 suivant les deux méthodes, que nous chronométrons :
Le résultat est sans appel, la boucle For est bien plus rapide à utiliser que la méthode récursive.
La raison est très simple, tandis que dans le premier cas, il ne s’agit que d’effectuer un calcul un grand nombre de fois, dans la seconde méthode, nous effectuons bien plus d’opérations : lancer une nouvelle procédure, effectuer un test If,…
Utiliser une boucle récursive peut parfois être une bonne idée, notamment pour raccourcir le code, le rendre plus clair et lisible, … il faut toutefois toujours garder en tête que celle-ci peut-être source de ralentissement (mais dans nos exemples, ces derniers sont absolument imperceptibles !)