Chapitre 16 — Routines définies par l'Utilisateur : PROC
Nous avons déjà vu qu'il existe des commandes et des fonctions intégrées qui sont la colonne vertébrale du
BASIC. On peut citer en exemple
CLS, qui nettoie l'écran, et
LOG(X)
qui renvoie le logarithme de X. Tôt ou tard, au fur et à mesure que les programmes s'allongent,
vous réaliserez que vous utilisez plusieurs fois les mêmes lignes de code dans un programme. Cette partie
montre comment nous pouvons créer nos propres commandes à partir de ces lignes communes et comment nous
pouvons les appeler ensuite à volonté.
BBC BASIC
fournit deux méthodes pour faire cela :
PROC et
FN.
Toutes deux permettent d'appeler une routine de la même façon que vous utilisez les commandes natives du
BASIC .
La différence, c'est que
PROC
est utilisée comme une commande (elle ne renvoie pas de valeur) alors que
FN
se comporte comme une fonction et renvoie une valeur que vous pouvez affecter à une expression
ou utiliser dans l'expression. Ces différences exceptées, la structure et les règles sont dans une large
mesure les mêmes, aussi traiterons-nous de
PROC
en premier lieu, mais souvenez-vous que beaucoup de ce qui va être dit s'appliquera aussi à
FN.
Supposez que vous vouliez vider l'écran et afficher un petit titre au haut de la page. C'est facile,
direz-vous, avec deux lignes de code :
CLS
PRINT TAB(2);"Guide pratique du BBC BASIC"
Eh bien, si nous voulons faire cela plusieurs fois, nous avons le choix entre copier ces lignes à chaque
fois, ou bien nous pouvons les inclure dans un bloc de code que nous pourrons appeler chaque fois que nous
le voudrons. Voici comment faire :
REM PROC démonstration
PROC_NouvelEcran
INPUT A$
PROC_NouvelEcran
END
DEF PROC_NouvelEcran
CLS
PRINT TAB(2);"Guide pratique du BBC BASIC"
ENDPROC
Tiens, à propos :
PROC signifie
« procédure ». Avant qu'une
PROC
ne puisse être appelée, elle doit être définie. Le début de la définition est signalé par le mot-clé
DEF
(qui signifie « DEFinir »).
La fin est signalée par
ENDPROC
(sans espace) et tout le code contenu entre les deux s'appelle le corps de la procédure. Quand le
programme tourne, il arrive à la ligne 2 et se souvient de l'endroit où il se trouve. Il saute ensuite
vers le corps de la
PROC
qu'il exécute. Lorsqu'il rencontre
ENDPROC,
il retourne là où il s'était interrompu et continue le programme comme précédemment. La même chose se passe
quand il arrive à la ligne 4.
La pratique courante est de placer les définitions de
PROC après
END,
c'est-à-dire après la fin du programme principal, afin que le code de ce programme ne soit pas parsemé de
déclarations. Le nom de la
PROC
suit les conventions pour l'attribution d'un nom aux variables : le premier caractère doit être
une lettre ou un souligné (on peut en fait aussi utiliser un chiffre, à la différence des variables)
et ensuite, n'importe quels caractères alphanumériques. La première lettre du nom doit suivre le mot
PROC,
sans aucun espace. C'est une pratique courante, bien que ce ne soit pas obligatoire, de commencer
par un souligné, juste pour rendre les choses un peu plus faciles à lire.
Donc, maintenant que nous pouvons afficher notre titre sur l'écran, que se passe-t-il si nous avons
des titres différents en fonction de l'écran que nous voulons afficher ? Eh bien, nous pourrions écrire une
PROC
différente pour chaque écran, mais si nous avons 10 écrans, ça fait beaucoup de code qui est répété
en plusieurs exemplaires.
« BB4W »
nous permet de passer une valeur à une
PROC
pour que cette
PROC
l'utilise de toutes les façons qu'elle peut trouver adaptée, aussi pouvons-nous passer un titre différent
comme chaîne de texte chaque fois que nous appelons la
PROC.
REM Passer une valeur à une PROC
PROC_NouvelEcran("Premier écran")
INPUT A$
PROC_NouvelEcran("Second écran")
END
DEF PROC_NouvelEcran(Titre$)
CLS
PRINT TAB(2);Titre$
ENDPROC
Quelle en est l'utilité ? Le texte du titre est copié dans une variable de type chaîne nommée
Titre$ et on s'en sert ensuite tout au long du corps de la routine, exactement comme si c'était
une variable normale. Notez que le contenu de Titre$ n'a de sens que pendant l'exécution de la
PROC.
On peut passer à une
PROC
autant de valeurs que l'on veut ; la règle est qu'une variable doit être du même type que celle
de la ligne où elle est déclarée, et les variables doivent apparaître dans le même ordre. Par exemple,
voici une variante qui nous permet de spécifier le titre et d'indiquer où il doit apparaître sur
la ligne du haut.
REM Passer une valeur à une PROC
PROC_NouvelEcran(5, "Premier écran")
INPUT A$
PROC_NouvelEcran(10, "Second écran")
END
DEF PROC_NouvelEcran(Col%,Titre$)
CLS
PRINT TAB(Col%);Titre$
ENDPROC
Lorsqu'une variable est passée à une
PROC,
cette procédure est libre de manipuler la valeur locale de toutes les façons qu'elle le veut. Comme elle
travaille avec une copie, la variable originale n'est pas changée. Cette méthode de passer des données
s'appelle « appel par valeur ».
REM Appel par valeur : démonstration
MaChaine$ = "Bonjour, tout le monde"
PRINT "Valeur avant PROC ";MaChaine$
PROC_FaireQuelqueChose(MaChaine$)
PRINT "Valeur après PROC ";MaChaine$
END
DEF PROC_FaireQuelqueChose(UneChaine$)
PRINT "Valeur passée à la PROC ";UneChaine$
UneChaine$="Adieu, monde cruel"
PRINT "Valeur après changement ";UneChaine$
ENDPROC
Il y a des cas où nous voulons que la routine fasse quelque chose de
permanent au contenu d'une variable.
Pour cela, nous utilisons le mot-clé
RETURN
dans la déclaration de la
PROC. Changez la ligne de
déclaration et relancez le programme pour voir les effets :
DEF PROC_FaireQuelqueChose(RETURN UneChaine$)
Ceci est nommé un « appel par référence ».
Vous pouvez passer l'intégralité de tableaux et de structures à une
PROC,
et pas seulement des variables à valeur unique. Pour ce faire, utilisez simplement le nom suivi de
parenthèses vides pour indiquer que ce n'est pas une bonne vieille variable quelconque à qui
nous avons affaire. Si vous vous souvenez de l'utilisation de
DIM
pour trouver la taille d'un tableau dans le chapitre sur les tableaux, vous pouvez à présent en voir
une utilisation immédiate. Si un tableau est passé, nous pouvons l'utiliser pour vérifier qu'il a
le nombre correct de dimensions de la bonne taille. Ceci aide à l'élimination d'erreurs dues
au dépassement des limites.
REM Pour passer un tableau
DIM TableauInt%(2,3)
PROC_TailleTablo(TableauInt%())
END
DEF PROC_TailleTablo(Tableau%())
PRINT "Ce tableau a ";
PRINT DIM(Tableau%());" dimensions."
ENDPROC
Les structures n'ont pas de fonction correspondante, aussi vous devez seulement connaître les membres
associés à cette structure.
REM Passer une structure
DIM MaStruct{A%,B$}
MaStruct.A%=23
MaStruct.B$="Vingt-trois"
PROC_AfficheStruct(MaStruct{})
END
DEF PROC_AfficheStruct(St{})
PRINT St.A%
PRINT St.B$
ENDPROC
Les tableaux et les structures sont toujours passés par référence. Ceci, parce que tableaux
ou structures peuvent être volumineux (et il en est fréquemment ainsi), c'est pourquoi on les utilise.
En faire une copie complète coûterait cher, à la fois en termes de mémoire et de temps de traitement que cela
prendrait pour copier physiquement chaque valeur. Cela signifie que si vous changez une valeur dans des
tableaux et des structures qui ont été passés, le changement est permanent.
REM Passer une structure
DIM MaStruct{A%,B$}
MaStruct.A%=23
MaStruct.B$="Vingt-trois"
PROC_AfficheStruct(MaStruct{})
PROC_AugmStruct(MaStruct{})
PROC_AfficheStruct(MaStruct{})
END
DEF PROC_AfficheStruct(St{})
PRINT St.A%
PRINT St.B$
ENDPROC
DEF PROC_AugmStruct(St{})
St.A%=24
St.B$="Vingt-quatre"
ENDPROC
Variables de type LOCAL
Savoir que nous pouvons passer des valeurs dans une
PROC
est une idée très importante ; cela signifie que nous pouvons écrire des routines qui sont portables de
programme à programme, aussi lorsque vous aurez testé une routine, vous pourrez la copier dans votre prochain
programme, la mettre dans une « bibliothèque » ou la donner à tous vos amis de
BBC BASIC
sans avoir à la développer à nouveau. Pour faire cela de la façon la plus efficace, nous devons nous assurer que
chaque variable utilisée dans la
PROC
n'est utilisée que dans la
PROC, et nulle part ailleurs.
On a déjà traité des valeurs passées depuis l'extérieur de la procédure. Ce dont nous avons besoin, c'est une
méthode pour que les variables appartiennent uniquement à une routine. Voilà le moment précis où nous vient
l'idée de variables locales.
Une variable locale est une variable qui existe au cours de l'exécution du corps d'une procédure
et à laquelle on ne peut accéder que pour cette procédure-là. Il existe un mot-clé, indispensable pour que le
BASIC
sache que la variable doit être traitée comme variable locale, et ce mot, c'est
LOCAL.
Une variable définie comme locale commence à exister quand une routine est appelée, peut être utilisée
n'importe où dans le corps du code et est rejetée quand on sort de la routine. Voyons un exemple, en supposant
que nous voulons afficher une rangée de caractères dans notre
PROC :
REM LOCAL démonstration 1
PROC_EcrisLigne("*")
END
DEF PROC_EcrisLigne(Car$)
LOCAL Compteur%
FOR Compteur% = 0 TO 79
PRINT Car$;
NEXT Compteur%
ENDPROC
Les variables locales suivent les conventions des variables normales (globales), en ce qui concerne
le nom et les types de définitions.
Vous pouvez aussi déclarer des tableaux et des structures en tant que
LOCAL,
mais une instruction
DIM
supplémentaire est indispensable pour y parvenir :
REM Tableau LOCAL
PROC_A
END
DEF PROC_A
LOCAL MonTableau%()
DIM MonTableau%(10)
REM Faire quelque chose...
ENDPROC
L'instruction
LOCAL
dit ici au
BASIC
qu'il lui faudra réserver de la place pour un tableau ; la ligne suivante avec le
DIM
lui donne la taille. Les structures
LOCAL
se font de la même manière :
REM Structure LOCAL
PROC_A
END
DEF PROC_A
LOCAL MaStruct{}
DIM MaStruct{a%,b,c$}
REM Faire quelque chose...
ENDPROC
Variables PRIVATE
Une fois que vous êtes familiarisé avec les variables
LOCAL,
les variables
PRIVATE
deviennent faciles à comprendre. Une variable
PRIVATE
se déclare de la même façon qu'une variable
LOCAL,
mais elle utilise le mot-clé
PRIVATE
pour sa définition. Elle est valide au cours de l'exécution d'une routine, mais pas à l'extérieur,
tout comme une variable
LOCAL.
La différence, c'est qu'une variable
PRIVATE
n'est pas oubliée lorsqu'une procédure se termine : sa valeur est maintenue en mémoire et réutilisée
la prochaine fois que la routine est appelée.
REM Variable PRIVATE
PRINT "Premier appel"
PROC_A
PRINT "Second appel"
PROC_A
END
DEF PROC_A
PRIVATE MonEnt%
PRINT "Valeur de MonEnt% = ";MonEnt%
MonEnt%=MonEnt% + 100
ENDPROC
Au premier passage, l'entier MonEnt% est créé et mis à zéro ; la valeur est affichée pour le
contrôle. MonEnt% est ensuite augmenté de 100. Au deuxième appel de
PROC_A, MonEnt% a une valeur retenue de l'appel précédent, et ceci est révélé par le nouvel
affichage de la valeur par la
PROC.
Portée (ou « Scope »)
Jusqu'à la rencontre de variables locales et privées, toutes les variables étaient accessibles n'importe où
à l'intérieur du programme : on les appelle variables « globales ». On peut
accéder aux variables globales à tout endroit du programme, même après avoir fini de le découper en sections
plus faciles à gérer avec
PROC
et
FN.
Si vous déclarez une variable dans une routine sans d'abord dire au
BASIC
si elle sera locale ou privée, cette variable sera globale, comme toutes les autres. Les programmeurs
(auxquels vous appartenez maintenant) appellent « portée » (ou, comme en anglais,
« scope ») cette « visibilité d'une variable »,
probablement parce que c'est plus facile à dire. Lorsqu'une variable n'est pas visible, on dit qu'elle est
« hors de portée » (« out of scope »).
Exercices
16.1
Modifiez la PROC_NouvelEcran pour qu'elle accepte une couleur d'arrière-plan et une couleur
de premier plan.
16.2
Écrivez et testez PROC_PlusGrand(A%,B%) qui compare A% et B%.
• Si A% > B%, ne faites rien ;
• si B% > A%, échangez les deux variables en vous servant d'une variable
locale. Il vous faudra passer par référence pour que le programme d'appel puisse afficher
les résultats.
Fin du Chapitre 16
|