| CHAPITRE
|
APPRENDRE
À PROGRAMMER AVEC FUTUREBASIC
|
NOTES
|
|
Structures conditionnelles IF THEN ELSE |
Leçon 9 : Structures
conditionnelles et boucles Le Compilateur analyse vos instructions en commençant par le début de votre code source jusquà la fin. Jen conviens cela paraît évident, mais ce qui lest moins, cest que les instructions réellement exécutées ne le seront pas forcément dans cet ordre-là. En réalité, le programme a un point dentrée et un point de sortie et entre ses deux points il a un flux, lart de la programmation consiste en grande partie à maîtriser ce flux. Supposez que votre programme renseigne une fiche détat civil et quil demande à lutilisateur de votre application de donner des informations le concernant, il ne serait pas très approprié de demander à un utilisateur masculin de saisir son nom de jeune fille. Pour éviter ce genre de situation maladroite, il serait bienvenu de pouvoir exécuter des instructions si certaines conditions sont remplies. Les structures conditionnelles répondent exactement à ce besoin. On peut considérer quil y a deux types de structures conditionnelles : les branchements conditionnels et les boucles. Linstruction la plus utilisée est la commande IF (signifiant « si » en anglais) qui permet de tester une condition. Elle est toujours accompagnée du mot-clé THEN (« alors » en anglais) qui stipule linstruction à exécuter si la condition est remplie : IF genre = _feminin THEN PRINT nomDeJeuneFille Dans linstruction ci-dessus, si la variable genre à cet endroit du programme contient la valeur de la constante que nous avons appelée _feminin alors linstruction à exécuter est dafficher le nom contenu dans la variable nomDeJeuneFille. Notez que le signe égal est ici le symbole de comparaison et non pas daffectation ; la variable genre ne se verra pas assigner une nouvelle valeur, et on aurait pu écrire ceci à la place pour éviter les risques de confusion : IF genre == _feminin THEN PRINT nomDeJeuneFille Lorsque FutureBASIC teste la condition, il évalue lexpression testée en lui attribuant la valeur 1 si la condition est remplie ou bien la valeur 0 dans le cas contraire. Vous reconnaissez sans doute ici les valeurs booléennes _zTrue et _false. En fait, FutureBASIC teste si lexpression nest pas fausse et attribue la valeur 0 ou 1 suivant les résultats de ce test. Ceci permet un raccourci décriture que vous rencontrerez très souvent dans des exemples de code : Il arrive très fréquemment que lon doive tester si le contenu dune variable est non nulle : IF maVariable <> 0 THEN PRINT maVariable Le même résultat peut-être obtenu en écrivant ceci : IF maVariable THEN PRINT maVariable Toute valeur de maVariable différente de 0 sera évaluée comme VRAI. La structure IF/THEN permet également dexécuter une instruction alternative au cas où la condition testée serait fausse. Ceci est réalisé grâce à la clause ELSE (signifiant « autrement » ou « sinon » en anglais) IF genre = _feminin THEN PRINT "Féminin" ELSE PRINT "Masculin" IF/THEN/ELSE est la plus simple des structures permettant de faire exécuter des commandes dépendant des conditions du programme à un instant donné. Cette structure peut se compliquer dans deux directions : Vous pourriez avoir besoin dexécuter plusieurs commandes si une condition est remplie, vous pourriez éventuellement écrire ceci : IF genre = _feminin THEN PRINT "Féminin" ELSE PRINT "Masculin" IF genre = _feminin THEN PRINT nomDeJeuneFille Mais, sachez que le langage vous autorise à exécuter plusieurs commandes dans une même ligne dinstruction. Les commandes doivent être alors séparées par le symbole « : », en conséquence le bout de code ci-dessus pourrait aussi bien sécrire ainsi : IF genre = _feminin THEN PRINT "Féminin" : PRINT nomDeJeuneFille ¬ ELSE PRINT "Masculin" Lautre direction dans laquelle une telle structure peut évoluer est relative à la nature du test à effectuer : vous aurez besoin parfois que plusieurs conditions soient remplies avant de déclencher une quelconque action : IF genre = _feminin THEN IF celibataire = _false THEN ¬ PRINT nomDeJeuneFille Dans une telle situation FutureBASIC propose un certains nombre dopérateurs pour évaluer les conditions. Les plus utilisés sont AND et OR. AND (et) retourne VRAI si les deux conditions sont remplies à la fois, OR (ou) retourne VRAI si lune au moins des conditions est remplie : IF genre = _feminin AND celibataire = _false THEN PRINT nomDeJeuneFille On devine que limbrication des tests et des commandes à exécuter peut rendre le code extrêmement pénible à déchiffrer. Vous préférerez de beaucoup utiliser la structure plus souple LONG IF/XELSE/END IF. Cette structure est essentiellement équivalente à la structure IF/THEN/ELSE, mais elle a lavantage de rendre le code plus lisible en distinguant bien les branchements, il est dailleurs recommandé de nécrire quune seule instruction par ligne, mais aussi elle est plus facilement gérable en permettant une plus grande facilité de modification : LONG IF genre = _feminin PRINT "Féminin" LONG IF celibataire = _false PRINT nomDeJeuneFille END IF XELSE PRINT "Masculin" END IF LEditeur indentera automatiquement les blocs dinstructions ce qui facilitera davantage la lecture du code. Notez la facilité pour imbriquer les branchements conditionnelles. Vous pouvez bien évidemment appliquer les mêmes tests avec ce genre de structure : LONG IF genre = _feminin AND celibataire = _false PRINT nomDeJeuneFille END IF Vous devez prêter une attention particulière avec les opérateurs logiques, car vous verrez quils ne sont pas toujours faciles à dompter et que nos erreurs de logique en la matière sont impitoyablement mises en évidence par lordinateur. SELECT CASE/END SELECT La structure SELECT CASE/END SELECT est plus sophistiquée quune structure LONG IF/END IF, car elle vous permet deffectuer des tests en série et lorsquune condition est remplie dexécuter des instructions appropriées. La structure est plus versatile, elle autorise plus de variations dans son écriture, mais son utilisation la plus courante peut être représentée par lexemple suivant : SELECT couleur CASE _zBlack PRINT "Noir" CASE _zCyan PRINT "Cyan" CASE _zYellow PRINT "Jaune" CASE zBlue,_zGreen PRINT "Cette couleur est réservée !" PRINT "Sélectionnez une autre couleur." CASE _zWhite PRINT "Blanc" CASE ELSE PRINT "Je ne connais pas cette couleur !" PRINT "Choisissez-en une autre SVP." END SELECT Ci-dessus, la variable couleur va être comparée à un ensemble de valeurs représentées ici par des constantes nommées. Dès que la valeur de la variable correspond à une des valeurs listées, le bloc dinstructions relatif à la clause CASE concernée est exécuté et le programme sortira ensuite de la structure. Notez deux choses ici, les valeurs à tester peuvent être regroupées dans une clause CASE si des traitements similaires doivent être opérés (CASE _zBlue, _zGreen), des actions par défaut peuvent être déclenchées si aucune correspondance na été trouvée entre la variable et les valeurs listées (CASE ELSE). Les mêmes opérations peuvent être accomplies avec des structures LONG IF/END IF, mais le flux du programme est plus difficile à suivre et le code plus délicat à modifier : LONG IF couleur = _zBlack PRINT "Noir" XELSE LONG IF couleur = _zCyan PRINT "Cyan" XELSE LONG IF couleur = _zYellow PRINT "Jaune" XELSE LONG IF couleur = _zblue OR couleur = _zGreen PRINT "Cette couleur est réservée !" PRINT "Sélectionnez une autre couleur." XELSE LONG IF couleur = _zWhite PRINT "Blanc" XELSE PRINT "Je ne connais pas cette couleur !" PRINT "Choisissez-en une autre SVP." END IF END IF END IF END IF END IF Les tests dans les clauses CASE, ne sont pas nécessairement des tests dégalité : SELECT valeur CASE > 0 PRINT "Positive" CASE < 0 PRINT "Négative" CASE ELSE PRINT "Nulle" END SELECT Vous nêtes pas obligé de singulariser une variable, les tests portent sur la véracité des expressions dans les clauses CASE. Considérez lexemple suivant : DIM AS SHORT a,b,c,d a = 4 b = 2 c = 3 d = 3 SELECT CASE a = 2 * b PRINT "a est égal à 2 fois b" CASE c = d PRINT "c est égal a d" CASE b = d PRINT "b est égal à d" END SELECT Ci-dessus, les expressions dans les clauses CASE vont être évaluées et dès que lun des résultats est VRAI, les instructions propres à cette clause sont exécutées. Avec les valeurs assignées aux variables dans notre exemple, le programme affichera « a est égal à 2 fois b » car lexpression a = 2 * b a été évaluée comme valant VRAI, en revanche et bien que la clause c = d puisse être également évaluée à VRAI (c et d valant tous les deux 3) le programme naffichera pas « c est égal à d ». Pour ceux qui auraient déjà programmé dans d'autres langages, notez qu'en BASIC, il n'est pas nécessaire d'inclure une instruction pour sortir explicitement d'un bloc d'instructions relatif à une clause CASE. Lorsqu'une des conditions est évaluée comme vraie, le bloc d'instructions qui s'y rapporte est exécuté, ensuite le programme suit son cours après l'instruction END SELECT à la fin du bloc structurel. Tout comme la structure LONG IF/END IF, les blocs dinstructions à lintérieur de chacune des clauses CASE peuvent eux-mêmes abriter dautres structures conditionnelles. Il est cependant bon déviter, quand on le peut, un trop grand niveau dimbrications si lon ne veut pas perdre le fil du raisonnement et le cheminement du programme. FOR/NEXT Les boucles sont aussi des structures conditionnelles car elles permettent dexécuter les mêmes instructions un nombre variable de fois en fonction de certaines conditions. Lorsque lon connaît ou bien lorsquon peut déduire le nombre de fois ou un bloc dinstructions doit être exécuté, on utilise plus généralement la boucle FOR/NEXT. La boucle FOR/NEXT initialise une variable de boucle qui est incrémentée à chaque fois que le bloc dinstructions a été exécuté. Lorsque cette variable atteint une valeur limite le programme quitte alors la boucle : FOR i = 1 TO 10 PRINT i * 7 NEXT Lorsque le programme entre dans la boucle ci-dessus, il attribue la valeur 1 à la variable de boucle i, puis exécute les instructions jusquà linstruction NEXT où il incrémente la valeur de la variable de boucle dune unité par défaut et compare le résultat à la valeur limite indiquée après le mots-clé TO. Si cette valeur nest pas encore dépassée, les instructions comprises entre FOR et NEXT sont à nouveau exécutées. Une fois que la valeur limite est dépassée, le programme continue après linstruction NEXT. Les boucles FOR/NEXT sont bien adaptées pour parcourir les éléments dune matrice en utilisant la variable de boucle comme indice du tableau : FOR i = 1 TO _nbEleves PRINT nomEleves(i) NEXT Les blocs dinstructions, comme dans toutes les structures conditionnelles, peuvent eux-mêmes contenir dautres structures conditionnelles. Ci-dessous deux boucles FOR/NEXT imbriquées pour parcourir un tableau à deux dimensions : FOR i = 1 TO _nbEleves FOR u = 1 TO _nbTrimestres PRINT notes(i,u) NEXT NEXT Une longue tradition veut que nous nommions très souvent les variables de boucle i,u et j, mais ce nest pas une obligation, tout autre nom de variable valide peut parfaitement faire laffaire. Lincrémentation de la variable de boucle est dune unité par défaut, mais vous pouvez préciser une valeur de pas différente avec la clause STEP : FOR i = 1 TO 10 STEP 2 PRINT i NEXT Ci-dessus la variable de boucle i est initialisée à 1 puis sera augmentée de 2 unités lorsque le programme atteindra linstruction NEXT. Le résultat produit affichera la séquence suivante : 1 3 5 7 9 Grâce à linstruction STEP, vous pouvez produire un décompte en lui attribuant une valeur négative: FOR i = 10 TO 1 STEP 1 PRINT i NEXT Il y a une chose importante à se rappeler concernant les boucles FOR/NEXT : à la différence de certains autres BASIC, FutureBASIC exécute au moins une fois les instructions à lintérieur de la boucle quelles que soient les conditions de départ : FOR i = 2 TO 1 PRINT i NEXT Même si la variable i (qui vaut 2 au démarrage de la boucle) est supérieure à la valeur limite (ici 1), la boucle est exécutée jusquà linstruction NEXT où la condition de sortie de boucle est testée. DO UNTIL / WHILE WEND Il y a de très nombreuses situations en programmation où lon ne connaît pas exactement le nombre ditérations à effectuer. Deux autres structures de boucles peuvent nous venir en aide pour les gérer. La boucle DO/UNTIL (faire jusquà ce que ) exécute un bloc dinstructions un nombre indéterminé de fois jusquà ce quune condition soit remplie. Tout comme avec la boucle FOR/NEXT, les instructions à lintérieur de la structure DO/UNTIL sont exécutées au moins une fois, le test sur la condition de fin de boucle étant aussi réalisé à la fin du bloc. Il est clair que la valeur qui servira à tester sil est temps de mettre un terme à la boucle devra être modifiée à lintérieur du bloc dinstructions, sinon la condition ne sera jamais remplie est lon créera une boucle infinie. Les boucles infinies sont une erreur fréquente et une plaie pour les débutants, car le programme exécute sans fin les mêmes instructions sans que lon puisse larrêter. Notez que le Runtime Console sécurisée vous permet dinterrompre un programme « coincé » dans une boucle infinie. i = 0 DO i = i + 1 PRINT i UNTIL i = 10 La boucle WHILE/WEND permet dexécuter un bloc dinstructions tant quune condition est remplie. Si cette condition nest pas satisfaite à lentrée de la boucle, les instructions ne sont pas exécutées : i = 0 WHILE i <= 10 i = i + 1 PRINT i WEND Dans lexemple ci-dessus, tant que la variable i reste inférieure ou égale à 10, elle est incrémentée, puis sa valeur est affichée. Dit autrement, une boucle DO/UNTIL sexécute tant que la condition finale reste fausse, tandis qu'une boucle WHILE/WEND sexécute tant que la condition initiale reste vraie. On a vu quil fallait sassurer que les conditions de sortie de boucle soient à un moment ou à un autre satisfaites pour éviter que le flux du programme ne soit piégé dans le trou noir dune boucle sans fin doù il ne pourra plus ressortir. Il arrive aussi, quon souhaite sortir prématurément dune boucle. Imaginez un programme qui vous permette de retrouver le nom dun élève dans une matrice de chaînes de caractères. Après cette recherche, vous souhaitez afficher la moyenne obtenue par cet élève. Examinez le code ci-dessous, et essayez den comprendre le raisonnement et le flux, vous devriez retrouver des notions que nous avons déjà abordées. On va supposer ici que les matrices notes et nom ont été remplies avec des valeurs par une autre partie du programme : _nbEleves = 40 _nbTrimestres = 4 DIM nom(_nbEleves) AS STR255 DIM notes(_nbEleves,_nbTrimestres) AS DOUBLE DIM nomATrouver AS STR255 DIM AS SHORT i, index nomATrouver = "Durand" index = 0 FOR i = 1 TO _nbEleves LONG IF nom(i) = nomATrouver index = i i = _nbEleves END IF NEXT LONG IF index PRINT "Moyenne de " + nomATrouver; PRINT notes(index,0) XELSE PRINT "Lélève "+ nomATrouver + " est introuvable." END IF Les déclarations des constantes et des tableaux ne devraient pas vous poser de problèmes. Les variables nomATrouver, i et index sont là pour illustrer notre petit module de recherche. Faites leffort dimaginer que les matrices nom (censée contenir le nom des élèves) et notes (censée contenir les notes obtenues par chacun des élèves) ont été assignées avec des valeurs par une partie du programme qui nest pas montrée ici. Le programme assigne une valeur à la variable nomATrouver, elle contiendra le nom que nous rechercherons dans le tableau contenant le nom des élèves. Évidemment, dans un programme plus évolué, ce nom est saisi par lutilisateur final du programme, mais nous verrons cela par la suite. La variable index est initialisée à la valeur 0 ; cette variable contiendra ultérieurement lindice du tableau nom où le nom de lélève que nous recherchons aura été trouvé. Après quoi, nous pouvons entrer dans la boucle qui va passer en revue tous les noms contenus dans la matrice et les comparer un à un au nom à trouver. La variable i est utilisée pour faire varier à chaque passage de la boucle lindice du tableau nom. Lorsquune correspondance a été trouvée, la valeur de la variable de boucle est stockée dans la variable index. Notre recherche ayant été fructueuse (et elle pourrait éventuellement lêtre dès le premier nom testé), on peut vouloir sortir de la boucle avant quelle ne se termine normalement, cest-à-dire après 40 itérations dans notre exemple. Inutile de faire travailler lordinateur pour rien. La façon traditionnelle en BASIC est dassigner à la variable de boucle sa valeur limite (i = _nbEleves) de telle sorte quà lexécution de linstruction NEXT suivante la variable i soit incrémentée et tombe en dehors des limites admises provoquant ainsi la sortie de la boucle. Au sortir de la boucle, nous pouvons alors tester la valeur de notre variable index et si elle est non nulle (rappelez-vous, IF index est équivalent à IF index <> 0), on peut alors afficher la moyenne obtenue en utilisant lindice que nous avons stocké dans la variable index durant lexécution de notre boucle FOR/NEXT. En revanche, si le contenu de la variable index est toujours égal à 0, cest que le flux de notre programme nest jamais passé par la partie de code qui modifie la valeur de cette variable, cela signifie quaucun des éléments contenus dans la matrice nom ne correspondait au nom que nous recherchions. En bref, le nom na pas été trouvé. Vous vous demandez peut-être pourquoi on assigne la valeur 0 à la variable index à lentrée de la boucle, puisque la variable recevra une valeur quand le nom recherché sera trouvé dans le tableau. Cette question amène une réponse qui mérite votre considération. Que se passerait-il si le nom recherché ne figurait pas dans le tableau ? Eh oui, une des règles à suivre pour programmer avec succès est de ne jamais supposer que les choses vont se passer comme on pourrait le souhaiter. Cette règle très simple, mais très importante, vous évitera bien des désagréments par la suite. Dans notre exemple donc, si nous ne trouvions aucune correspondance de nom, le flux du programme exécuterait la boucle une quarantaine de fois sans jamais assigner de valeur à notre variable index. La boucle sachèverait donc avec une variable index dont la valeur serait indéterminée. Les instructions qui manipulent des variables au contenu indéterminé sont susceptibles de fournir des résultats pour le moins inattendus, certainement erronés et peut-être même catastrophiques. Cest ici loccasion dapprendre ou de réviser une chose essentielle : lorsque vous déclarez une variable avec linstruction DIM, le Compilateur va réserver un bloc de mémoire dune taille adéquate au type de variable déclarée, mais en aucun cas il nassigne de valeur à cette variable fraîchement créée. En réalité, le Compilateur cherche une place disponible en mémoire où la variable pourra se loger, et quand cette place est trouvée, il réserve le bloc qui pourrait éventuellement avoir contenu des données qui ne sont plus utilisées au moment où la variable est déclarée, et dans ce cas de figure, la variable contiendra une valeur sans signification mais néanmoins bien réelle. Moralité, il ne faut pas assumer quune variable nouvellement créée contient une valeur nulle. Cest pourquoi, nous devons ici initialiser explicitement notre variable index à 0. Pour contourner le piège possible que nous venons de mentionner, nous pouvons utiliser linstruction EXIT qui autorise le flux du programme à quitter toutes sortes de structures. Chaque structure de boucle dispose de sa propre version de la commande : EXIT DO ou EXIT UNTIL ; EXIT WHILE ou EXIT WEND ; EXIT FOR ou EXIT NEXT et même EXIT CASE. Le programme précédent pourrait donc sécrire plus simplement de la façon suivante : _nbEleves = 40 _nbTrimestres = 4 DIM nom(_nbEleves) AS STR255 DIM notes(_nbEleves,_nbTrimestres) AS DOUBLE DIM nomATrouver AS STR255 DIM AS SHORT i nomATrouver = "Durand" FOR i = 1 TO _nbEleves IF nom(i) = nomATrouver THEN EXIT FOR NEXT LONG IF i <= _nbEleves PRINT "Moyenne de " + nomATrouver; PRINT notes(i,0) XELSE PRINT "Elève introuvable" END IF Ci-dessus, la boucle FOR/NEXT sachèvera soit après 40 itérations, soit si le nom recherché est trouvé dans le tableau que nous examinons. Si aucun nom nest trouvé, la variable de boucle i vaudra 40 au dernier passage de la boucle et lorsque linstruction NEXT est exécutée, elle sera incrémentée dune unité et prendra alors la valeur 41; dans le cas contraire, le flux du programme est dirigé à lextérieur de la boucle après l'instruction NEXT. Le test suivant consistera à examiner le contenu de la variable de boucle, si la valeur ne dépasse pas 40 (inférieure ou égale à 40) cest que le nom aura été trouvé dans le tableau et lon peut agir en conséquence. Ce petit exemple montre tout dabord quil ny a pas un chemin unique pour résoudre un problème. Vous aurez loccasion de constater par la suite que très souvent les chemins les plus directs sont souvent les plus efficaces. Une bonne pratique à adopter très tôt est de penser en termes déconomie de moyens. Réduire le nombre de variables et simplifier le flux du programme sont des objectifs qui vous conduiront à réaliser des applications plus fluides, plus rapides, moins gourmandes en ressources, en un mot plus efficaces. Si dans notre exemple, cela ne fait pas une grande différence, il nen sera pas de même lorsque vous écrirez des programmes de plusieurs milliers de lignes de code avec des algorithmes exécutés des dizaines de milliers de fois. [Precédent] [Table des Matières] [Suivant] |
{Note} |
|
FutureBASIC est une marque déposée appartenant à Staz Software, Inc et utilisée avec permission. |
||