Voir le flux RSS

User

[Actualité] Traçage des appels récursifs en VBA

Noter ce billet
par , 27/03/2019 à 20h20 (454 Affichages)
Il est souvent compliqué de déboguer les procédures récursives, c'est pourquoi je vous propose un jeu de procédures permettant de tracer les différents appels récursifs sous la forme d'une structure d'arbre.

Ce traçage des différents appels numérotés et regroupés par niveau, permet ainsi de mieux visualiser le déroulé de la procédure.
Ce résultat peut être affiché dans la fenêtre d'exécution ou dans un fichier de sortie au format texte.

  • Deux procédures balises BeginSub et EndSub, permettant de délimiter le début et la fin de la procédure.
  • Une procédure PrintDebug pour afficher un message associé à chaque appel, à positionner au bon endroit dans le code de la procédure entre les 2 balises.


Exemple d'écriture dans la fonction récursive Combinaisons :

Code VBA : 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
Function Combinaisons(x As Long, y As Long) As Long
    ' Code de la fonction Combinaisons avec les différentes balises
    On Error GoTo erreur_Combinaisons
 
    BeginSub
 
    ' On imprime du texte sur le traçage
    PrintDebug "(" & x & "," & y & ")"
 
        If (x = y) Or (y = 0) Then
            Combinaisons = 1
        Else
            If (y = 1) Then
                Combinaisons = x
            Else
                Combinaisons = Combinaisons(x - 1, y - 1) + Combinaisons(x - 1, y)
            End If
        End If
 
    ' On imprime du texte sur le traçage
    PrintDebug "(Combin(" & x & "," & y & ")=" & Combinaisons & ")"
 
    EndSub
 
erreur_Combinaisons:
 
        If Err.Number <> 0 Then
            StopDebug ' Gestion de l'erreur : arrêt du traçage
            On Error GoTo 0 ' Annule la gestion d'erreur
            Resume ' Retourne sur la ligne qui a déclenché l'erreur
        End If
 
End Function


Une procédure InitDebug pour initialiser les paramètres de traçage avant de lancer la procédure à déboguer.

Elle peut prendre en option les arguments :

  • nbCalls : Nombre d'appels total à afficher en sortie ;
  • lgColumn : largeur des colonnes en nombre de caractères ;
  • tpOut : type de sortie (Fenêtre_Execution ou Fichier_Texte).


Exemple de test pour la fonction Combinaisons :

Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
Public Function TestCombinaisons(x As Long, y As Long)
    ' Exemple de test pour la fonction Combinaisons
 
    InitDebug ' Initialisation des paramètres par défaut
    Call Combinaisons(x, y) ' Exécution de la fonction
 
End Function

Résultat pour TestCombinaisons(5,3) :

Nom : test_combinaisons.jpg
Affichages : 490
Taille : 57,1 Ko

Le module complet comprend également une fonction de sortie StopDebug pour afficher le résultat y compris en cas d'erreur :
Code VBA : 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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
Option Explicit
 
' Module pour tracer les appels récursifs
' User - 20/03/2019
 
' Type énumératif pour le type de sortie
Public Enum TypeSortie
    Fenetre_Execution = 1
    Fichier_texte = 2
End Enum
 
' Chaîne de caractères contenant le résultat en sortie
Dim stSortie As String
 
' Indice de colonne
Dim idColonne As Long
Dim flag As Boolean
 
' Type de sortie
Dim tpSortie As TypeSortie
 
Dim nbAppels As Long ' Nombre total d'appels du traçage, si 0 alors on va à la fin de la procédure
Dim idAppel As Long ' Indice d'appel
 
' Largeur des colonnes en sortie
Dim lgColonne As Long
 
Public Function InitDebug(Optional nbCalls As Long = 0, Optional lgColumn As Long = 15, Optional tpOut As TypeSortie = 1)
    ' Initialisation des paramètres de traçage ; nbCalls : nombre d'appels ; lgColumn : largeur des colonnes du tableau;
    ' tpOut : Type de sortie (fenêtre d'exécution, fichier texte)
 
    idColonne = 0 ' Indice de colonne
 
    idAppel = 0 ' On inilialise le compteur d'appels
 
    lgColonne = lgColumn ' Largeur des colonnes en nombre de caractères
    tpSortie = tpOut ' Type de sortie (fenêtre d'exécution ou fichier texte)
 
    stSortie = vbNullString ' Chaîne contenant le résultat en sortie
    nbAppels = nbCalls ' On initialise le nombre d'appels maxi
    flag = True ' le flag permet de marquer le début et la fin du traçage
 
End Function
 
Public Sub BeginSub()
    ' Marqueur du début dans le code de la procédure
    idAppel = idAppel + 1 ' On incrémente le compteur d'appels
 
        ' On affiche en sortie un appel de procédure
        If idAppel = 1 Then ' Si 1er appel
            stSortie = String(idColonne * lgColonne, " ") & "Apple n°" & idAppel & "->"  ' On imprime Appel n°1
        Else ' sinon on imprime Appel n°_ avec un décallage de idColonne
            stSortie = stSortie & vbNewLine & String(idColonne * lgColonne, " ") & "Apple n°" & idAppel & "->"
        End If
 
    idColonne = idColonne + 1 ' On incrémente l'indice de colonne
 
End Sub
 
Public Sub EndSub()
    ' Marqueur de fin dans le code de la procédure
 
    idColonne = idColonne - 1 ' On remonte les appels
 
        If flag Then
 
            If (idColonne = 0) Then   ' si traçage complet terminé
                flag = False ' indicateur de fin de traçage
                StopDebug ' procédure d'arrêt du traçage
            Else
                If ((idAppel >= nbAppels) And (nbAppels > 0)) Then ' si nombre d'appels demandés dépassé
                    flag = False ' indicateur de fin de traçage
                    StopDebug ' procédure d'arrêt du traçage
                End If
            End If
 
        End If
 
End Sub
 
Sub PrintDebug(s As String)
    'Ajoute dans la chaîne de sortie le contenu du texte à afficher sur la bonne colonne
    stSortie = stSortie & vbNewLine & String(idColonne * lgColonne, " ") & s ' La fonction String permet de décaller le texte
 
End Sub
 
 
Public Function Factorielle(n As Long) As Long
On Error GoTo erreur_Factorielle
BeginSub ' Marqueur de début de procédure
 
    ' On imprime du texte sur le traçage
    PrintDebug "(n=" & n & ")"
 
        If n = 0 Then
            Factorielle = 1
        Else
            Factorielle = n * Factorielle(n - 1)
        End If
 
    ' On imprime du texte sur le traçage
    PrintDebug "(Fact(" & n & ")=" & CStr(Factorielle) & ")"
 
EndSub ' Marqueur de fin de procédure
 
erreur_Factorielle:
 
        If Err.Number <> 0 Then
            StopDebug ' Gestion de l'erreur : arrêt du traçage
            On Error GoTo 0 ' Annule la gestion d'erreur
            Resume ' Retourne sur la ligne qui a déclenché l'erreur
        End If
 
End Function
 
Function Fibonacci(n As Long) As Long
    ' Code de la fonction Fibonacci avec les différentes balises
    On Error GoTo erreur_Fibonacci
 
    BeginSub
 
    ' On imprime du texte sur le traçage
    PrintDebug "(n=" & n & ")"
 
        If (n = 0) Then
            Fibonacci = 0
        Else
            If (n = 1) Then
                Fibonacci = 1
            Else
                Fibonacci = Fibonacci(n - 2) + Fibonacci(n - 1)
            End If
        End If
 
    ' On imprime du texte sur le traçage
    PrintDebug "(Fibo(" & n & ")=" & Fibonacci & ")"
 
    EndSub ' Marquage de fin de la procédure
 
erreur_Fibonacci:
 
        If Err.Number <> 0 Then
            StopDebug ' Gestion de l'erreur : arrêt du traçage
            On Error GoTo 0 ' Annule la gestion d'erreur
            Resume ' Retourne sur la ligne qui a déclenché l'erreur
        End If
 
End Function
 
Function Combinaisons(x As Long, y As Long) As Long
    ' Code de la fonction Combinaisons avec les différentes balises
    On Error GoTo erreur_Combinaisons
 
    BeginSub
 
    ' On imprime du texte sur le traçage
    PrintDebug "(" & x & "," & y & ")"
 
        If (x = y) Or (y = 0) Then
            Combinaisons = 1
        Else
            If (y = 1) Then
                Combinaisons = x
            Else
                Combinaisons = Combinaisons(x - 1, y - 1) + Combinaisons(x - 1, y)
            End If
        End If
 
    ' On imprime du texte sur le traçage
    PrintDebug "(Combin(" & x & "," & y & ")=" & Combinaisons & ")"
 
    EndSub
 
erreur_Combinaisons:
 
        If Err.Number <> 0 Then
            StopDebug ' Gestion de l'erreur : arrêt du traçage
            On Error GoTo 0 ' Annule la gestion d'erreur
            Resume ' Retourne sur la ligne qui a déclenché l'erreur
        End If
 
End Function
 
Public Function TestFactorielle(n As Long)
    ' Exemple de test pour la fonction Factorielle
 
    InitDebug ' Initialisation des paramètres par défaut
    Call Factorielle(n) ' Exécution de la fonction
 
End Function
 
Public Function TestFibonacci(n As Long)
    ' Exemple de test pour la fonction de Fibonacci
 
    InitDebug , 20, Fichier_texte ' Initialisation des paramètres
    Call Fibonacci(n) ' Exécution de la fonction
 
End Function
 
Public Function TestCombinaisons(x As Long, y As Long)
    ' Exemple de test pour la fonction Combinaisons
 
    InitDebug , , Fichier_texte ' Initialisation des paramètres par défaut
    Call Combinaisons(x, y) ' Exécution de la fonction
 
End Function
 
Public Sub CreateTextFile(chemin, s As String)
    ' Procédure de création du fichier texte
    Open chemin For Output As #1
    Print #1, s
    Close #1
End Sub
 
Public Sub StopDebug()
    ' Procédure d'arrêt du traçage
    Dim chemin As String
 
        If (tpSortie = Fenetre_Execution) Then ' Si la sortie se fait dans la fenête d'exécution
            Debug.Print stSortie
        Else
            If (tpSortie = Fichier_texte) Then
                chemin = CurDir & "\Sortie.txt"
                CreateTextFile chemin, stSortie ' Création du fichier Texte
                Call Shell("notepad.exe """ & chemin & """", vbNormalFocus) ' Ouverture du fichier avec notepad
            End If
        End If
 
        If (nbAppels > 0) And Not (flag) Then ' Si un nombre appels limité a été demandé ou si on est pas sorti sur une erreur
            End ' stop la procédure principale et initialise les variables
        End If
 
End Sub

Il peut-être copié puis collé dans un fichier Excel ou Access. Il ne contient pas de référence externe, la structure d'arbre étant simplement copiée dans une chaîne de caractères ou dans un fichier texte.
Attention à ne pas passer un argument trop élevé pour les fonctions récursives qui pourrait provoquer des empilements d'appels trop importants en mémoire.


Voilà, j'ai trouvé l'idée intéressante

Bons tests,
Cordialement,

Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Viadeo Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Twitter Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Google Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Facebook Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Digg Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Delicious Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog MySpace Envoyer le billet « Traçage des appels récursifs en VBA » dans le blog Yahoo

Mis à jour 29/03/2019 à 10h20 par User

Catégories
Programmation , VBA

Commentaires