[EN] [IT]
[INFO] [ACQUÉRIR] [PLAN] [RESSOURCES]
CHAPITRE
APPRENDRE À PROGRAMMER AVEC FUTUREBASIC
NOTES

Structures conditionnelles

IF THEN ELSE
LONG IF XELSE END IF
SELECT CASE END SELECT
FOR/NEXT
DO/UNTIL
WHILE/WEND

Leçon 9 : Structures conditionnelles et boucles

IF THEN ELSE / LONG IF XELSE END IF
Le Compilateur analyse vos instructions en commençant par le début de votre code source jusqu’à la fin. J’en conviens cela paraît évident, mais ce qui l’est moins, c’est 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 d’entrée et un point de sortie et entre ses deux points il a un flux, l’art de la programmation consiste en grande partie à maîtriser ce flux.

Supposez que votre programme renseigne une fiche d’état civil et qu’il demande à l’utilisateur 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 qu’il y a deux types de structures conditionnelles : les branchements conditionnels et les boucles.

L’instruction 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 l’instruction à exécuter si la condition est remplie :

IF genre = _feminin THEN PRINT nomDeJeuneFille

Dans l’instruction ci-dessus, si la variable genre à cet endroit du programme contient la valeur de la constante que nous avons appelée _feminin alors l’instruction à exécuter est d’afficher le nom contenu dans la variable nomDeJeuneFille. Notez que le signe égal est ici le symbole de comparaison et non pas d’affectation ; 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 l’expression 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 l’expression n’est 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 l’on doive tester si le contenu d’une 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 d’exé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 d’exé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 d’instruction. 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"


L’autre 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 d’opé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 l’une au moins des conditions est remplie :

IF genre = _feminin AND celibataire = _false THEN PRINT nomDeJeuneFille

On devine que l’imbrication 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 l’avantage de rendre le code plus lisible en distinguant bien les branchements, il est d’ailleurs recommandé de n’écrire qu’une 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


L’Editeur indentera automatiquement les blocs d’instructions 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 qu’ils ne sont pas toujours faciles à dompter et que nos erreurs de logique en la matière sont impitoyablement mises en évidence par l’ordinateur.

SELECT CASE/END SELECT
La structure SELECT CASE/END SELECT est plus sophistiquée qu’une structure LONG IF/END IF, car elle vous permet d’effectuer des tests en série et lorsqu’une condition est remplie d’exé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 l’exemple 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 d’instructions 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 n’a é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 l’exemple 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 l’un 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 l’expression 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 n’affichera 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 d’instructions à l’intérieur de chacune des clauses CASE peuvent eux-mêmes abriter d’autres structures conditionnelles. Il est cependant bon d’éviter, quand on le peut, un trop grand niveau d’imbrications si l’on 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 d’exécuter les mêmes instructions un nombre variable de fois en fonction de certaines conditions.

Lorsque l’on connaît ou bien lorsqu’on peut déduire le nombre de fois ou un bloc d’instructions 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 d’instructions 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’à l’instruction NEXT où il incrémente la valeur de la variable de boucle d’une unité par défaut et compare le résultat à la valeur limite indiquée après le mots-clé TO. Si cette valeur n’est 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 l’instruction NEXT.

Les boucles FOR/NEXT sont bien adaptées pour parcourir les éléments d’une matrice en utilisant la variable de boucle comme indice du tableau :

FOR i = 1 TO _nbEleves
  PRINT nomEleves(i)
NEXT


Les blocs d’instructions, comme dans toutes les structures conditionnelles, peuvent eux-mêmes contenir d’autres 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 n’est pas une obligation, tout autre nom de variable valide peut parfaitement faire l’affaire.

L’incrémentation de la variable de boucle est d’une 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 l’instruction NEXT. Le résultat produit affichera la séquence suivante :

1
3
5
7
9


Grâce à l’instruction 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 à l’inté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’à l’instruction 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ù l’on ne connaît pas exactement le nombre d’ité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 d’instructions un nombre indéterminé de fois jusqu’à ce qu’une condition soit remplie. Tout comme avec la boucle FOR/NEXT, les instructions à l’inté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 s’il est temps de mettre un terme à la boucle devra être modifiée à l’intérieur du bloc d’instructions, sinon la condition ne sera jamais remplie est l’on 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 l’on puisse l’arrêter. Notez que le Runtime Console sécurisée vous permet d’interrompre un programme « coincé » dans une boucle infinie.

i = 0
DO
  i = i + 1
  PRINT i
UNTIL i = 10


La boucle WHILE/WEND permet d’exécuter un bloc d’instructions tant qu’une condition est remplie. Si cette condition n’est pas satisfaite à l’entrée de la boucle, les instructions ne sont pas exécutées :

i = 0
WHILE i <= 10
  i = i + 1
  PRINT i
WEND


Dans l’exemple 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 s’exécute tant que la condition finale reste fausse, tandis qu'une boucle WHILE/WEND s’exécute tant que la condition initiale reste vraie.

On a vu qu’il fallait s’assurer 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 d’une boucle sans fin d’où il ne pourra plus ressortir. Il arrive aussi, qu’on souhaite sortir prématurément d’une boucle.

Imaginez un programme qui vous permette de retrouver le nom d’un é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 d’en 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 l’effort d’imaginer 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 n’est 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 l’utilisateur final du programme, mais nous verrons cela par la suite.

La variable index est initialisée à la valeur 0 ; cette variable contiendra ultérieurement l’indice 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 l’indice du tableau nom.

Lorsqu’une 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 qu’elle ne se termine normalement, c’est-à-dire après 40 itérations dans notre exemple. Inutile de faire travailler l’ordinateur pour rien. La façon traditionnelle en BASIC est d’assigner à la variable de boucle sa valeur limite (i = _nbEleves) de telle sorte qu’à l’exécution de l’instruction 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 l’indice que nous avons stocké dans la variable index durant l’exécution de notre boucle FOR/NEXT. En revanche, si le contenu de la variable index est toujours égal à 0, c’est que le flux de notre programme n’est jamais passé par la partie de code qui modifie la valeur de cette variable, cela signifie qu’aucun des éléments contenus dans la matrice nom ne correspondait au nom que nous recherchions. En bref, le nom n’a pas été trouvé.

Vous vous demandez peut-être pourquoi on assigne la valeur 0 à la variable index à l’entré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 s’achè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. C’est ici l’occasion d’apprendre ou de réviser une chose essentielle : lorsque vous déclarez une variable avec l’instruction DIM, le Compilateur va réserver un bloc de mémoire d’une taille adéquate au type de variable déclarée, mais en aucun cas il n’assigne 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 qu’une variable nouvellement créée contient une valeur nulle. C’est pourquoi, nous devons ici initialiser explicitement notre variable index à 0.

Pour contourner le piège possible que nous venons de mentionner, nous pouvons utiliser l’instruction 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 s’achèvera soit après 40 itérations, soit si le nom recherché est trouvé dans le tableau que nous examinons. Si aucun nom n’est trouvé, la variable de boucle i vaudra 40 au dernier passage de la boucle et lorsque l’instruction NEXT est exécutée, elle sera incrémentée d’une unité et prendra alors la valeur 41; dans le cas contraire, le flux du programme est dirigé à l’exté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) c’est que le nom aura été trouvé dans le tableau et l’on peut agir en conséquence.

Ce petit exemple montre tout d’abord qu’il n’y a pas un chemin unique pour résoudre un problème. Vous aurez l’occasion 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 n’en 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}
  © 2000 Pix&Mix  
  Tous droits réservés
INFO  |  ACQUÉRIR |  PLAN  |  RESSOURCES

  FutureBASIC est une marque déposée appartenant à Staz Software, Inc et utilisée avec permission.