VIII. Structures de programmation en Pascal▲
Jusqu'à présent, lorsque nous avons écrit des instructions (dans une procédure ou
dans une fonction, peu importe), celles-ci étaient exécutées les unes après les
autres. Cela ne posait pas de problème puisque les exemples étaient choisis pour
cela, mais rapidement, il va nous falloir structurer les instructions pour répondre à
des besoins spécifiques tels des conditions sur une ou plusieurs valeurs, des
parcours d'éléments de listes (par exemple ajouter 1 à tous les éléments d'un tableau
dont les dimensions dépendent d'une donnée calculée pendant le programme, ...
Pour répondre à l'ensemble de nos besoins, un certain nombre de structures
élémentaires permettent de répondre à tout. Il faudra parfois être astucieux et
combiner ces structures pour en tirer un fonctionnement adéquat, mais il y aura moyen
répondre quasiment à n'importe quel besoin. Ces structures se répartissent en deux
familles : les structures conditionnelles et les structures itératives.
Chacune des structures que nous allons voir dans ce chapitre prend la place d'une
unique instruction. Cette instruction structurée autour de mots réservés et d'une
syntaxe particulière à chaque bloc contient la plupart du temps une ou plusieurs
autres instructions ou blocs d'instructions (qui contiennent eux-mêmes des
instructions). Il est à noter que tous ces blocs peuvent s'imbriquer les uns dans les
autres, pour donner des comportements plus élaborés que ceux des blocs de base : ceci
sera l'objet d'un paragraphe.
Ce chapitre est des plus importants, pourtant, malgré la pléthore de notions
intéressantes et importantes qu'il présente, sa longueur également risque de vous
paraître... importante. J'ai tenté de résoudre ce problème en insérant quelques
exercices et manipulations.
Chacune des structures évoquées dans ce chapitre sera présentée indépendament des
autres, pourtant, ces structures pourront être « imbriquées » les unes dans les
autres et c'est précisément cet art de manipuler ces structures qui vous permettra de
programmer efficacement.
VIII-A. Structures conditionnelles▲
Le langage Pascal offre deux structures conditionnelles différentes : les structures if et les structures case. Elles sont employées à des endroits différents : tout dépend de ce que l'on veut faire. Les blocs if permettent de tester la valeur d'un booléen et d'exécuter une ou des instructions suivant que la valeur est vraie ou fausse. Les blocs case, bien que plus complexes, sont plus puissants puisqu'ils permettent de spécifier plusieurs cas, et même un cas général qui complète les autres cas. Ces deux structures vont faire chacune l'objet d'une étude détaillée.
VIII-A-1. Blocs 'if'▲
Les blocs if existent sous deux formes. La première, plus simple que l'autre, est :
if
valeur_booleenne then
instruction;
Ce bloc fonctionne de façon relativement simple (principe du si...alors) : si
'valeur_booleenne' vaut 'true', alors instruction est exécutée.
Valeur_booleenne représente n'importe quoi qui donne un booléen en
définitive, que ce soit directement un booléen, une comparaison (à l'aide des
opérateurs de comparaison), le résultat d'une fonction, ou une combinaison d'un ou
plusieurs de ces éléments avec les opérateurs booléens. instruction désigne
au choix une instruction unique, ou alors un bloc d'instructions délimité par les
désormais habituels begin et end.
Voici comme d'habitude un exemple utilisable sous Delphi (toujours au même endroit) :
procedure
TForm1.Button1Click(Sender: TObject);
var
DitBonjour: Boolean
;
begin
DitBonjour := false
;
if
DitBonjour then
ShowMessage('Bonjour !'
);
end
;
Cet exemple est des plus simples : une variable booléenne est déclarée et
initialisée. Un bloc if utilise la valeur de cette variable comme condition. Si la
valeur de départ est 'true', le message est affiché (sinon, il ne l'est pas).
Essayez cet exemple sous Delphi en changeant la valeur de départ de 'DitBonjour' de
'false' à 'true'.
Cet exemple manquant un peu de piment, nous allons un peu le compliquer :
procedure
TForm1.Button1Click(Sender: TObject);
const
MotDePasse = 'machin'
;
var
Reponse: string
;
begin
Reponse := ''
;
InputQuery('Attention'
, 'Entrez le mot de passe'
, Reponse);
if
Reponse = MotDePasse then
ShowMessage('Mot de passe correct'
);
end
;
Cet exemple fait usage de la fonction 'InputQuery'. Cette fonction, qui montre une fenêtre contenant une zone d'édition, du texte, et deux boutons 'OK' et 'Annuler', permet de poser une question à l'utilisateur. Voici celle qui apparaîtra sur votre écran :
Elle admet 3 paramètres : le premier est le texte apparaissant dans la barre de
titre de la fenêtre. Le second est le texte affiché juste au-dessus de la zone
d'édition. Le troisième est un peu particulier dans le sens où on doit absolument
transmettre une variable et pas une valeur. Cette variable, de type chaîne, est
utilisée de deux manières : la valeur de la variable est affichée dans la boite de
dialogue lorsque celle-ci est montrée à l'utilisateur. La valeur de la variable sera
ensuite modifiée par la fonction et recevra le texte entré par l'utilisateur si ce
dernier clique sur 'OK'. La fonction renvoie un résultat booléen : si l'utilisateur
clique sur 'OK', la fonction renvoie 'true', sinon elle renvoie 'false'.
Hormis l'usage de cette fonction, la procédure initialise d'abord la variable
'Reponse'. Ainsi, rien ne sera affiché dans la zone d'édition de la boite de
dialogue. Vient ensuite un bloc if : le contenu de 'Reponse' est comparé à la
constante 'MotDePasse'. Si les deux chaînes sont identiques, alors un message
indique à l'utilisateur que le mot de passe est correct (si l'utilisateur donne un
mauvais mot de passe, ou s'il annule, les deux chaînes seront différentes et rien ne
se passera).
L'exemple ci-dessous est encore un peu plus délicat :
procedure
TForm1.Button1Click(Sender: TObject);
const
MotDePasse = 'machin'
;
var
Reponse: string
;
DonneReponse: boolean
;
begin
Reponse := ''
;
DonneReponse := InputQuery('Attention'
, 'Entrez le mot de passe'
, Reponse);
if
not
DonneReponse then
ShowMessage('Vous deviez indiquer un mot de passe !'
);
if
DonneReponse and
(Reponse = MotDePasse) then
ShowMessage('Mot de passe correct'
);
if
DonneReponse and
(Reponse <> MotDePasse) then
ShowMessage('Mot de passe incorrect'
);
end
;
Le premier bloc if utilise un opérateur booléen pour obtenir la négation de
'DonneReponse' : on montre le message d'erreur seulement lorsque 'not DonneReponse'
vaut 'true', c'est-à-dire lorsque 'DonneReponse' vaut 'false' (habituez-vous dés à
présent à ces raisonnements logiques, ils sont très fréquents et utiles dans les
blocs if). Le deuxième bloc if utilise une condition plus fine
puisqu'il y a un opérateur booléen entre deux valeurs booléennes : la premiere est
la valeur d'une variable booléenne, tandis que la seconde est le résultat de la
comparaison entre 'Reponse' et 'MotDePasse'. Le message n'est donc affiché que si
l'utilisateur donne une réponse, et si sa réponse correspond au bon mot de passe. Le
troisième bloc if est du même genre mais le message n'est affiché que lorsque
l'utilisateur donne un mot de passe éronné.
Il est maintenant opportun de voir la deuxième forme des blocs if. Cette
forme permet de réagir avec le principe suivant : si...alors...sinon :
if
valeur_booleenne then
instruction_si_vrai
else
instruction_si_faux;
instruction_si_vrai et instruction_si_faux sont chacun soit une instruction seule soit un bloc d'instructions. Les instructions incluses dans instruction_si_vrai sont exécutées si la valeur_booleenne est 'true', tandis que celles incluses dans instruction_si_faux sont exécutées dans le cas contraire (si valeur_booleenne est 'false'). Cette forme permettra de réagir différemment suivant les deux valeurs en une seule instruction. (Il est à noter que la même chose serait possible en utilisant deux blocs if qui testeraient successivement une valeur et sa négation par l'opérateur not. L'utilisation de if...then...else sera pourtant préférée à l'utilisation de deux blocs if)
Voici le schémas de fonctionnement d'un bloc if :
Ce schéma comporte ce qui se rapporte au langage en rouge (les mots-clés sont en
gras), la partie facultative (else) marquée en vert, ainsi que le principe général
indiqué en noir : suivant une condition, il y a branchement vers une instruction ou
bloc d'instruction différente suivant que la condition est réalisée ou pas.
Le dernier exemple a été réécrit ci-dessous en utilisant la nouvelle syntaxe (avec
partie 'else') :
procedure
TForm1.Button1Click(Sender: TObject);
const
MotDePasse = 'machin'
;
var
Reponse: string
;
DonneReponse: boolean
;
begin
Reponse := ''
;
DonneReponse := InputQuery('Attention'
, 'Entrez le mot de passe'
, Reponse);
if
DonneReponse then
begin
if
Reponse = MotDePasse then
ShowMessage('Mot de passe correct'
)
else
ShowMessage('Mot de passe incorrect'
);
end
else
ShowMessage('Vous deviez indiquer un mot de passe !'
);
end
;
Cet exemple montre plein de nouveautés : deux blocs if...then...else sont utilisés,
et sont même imbriqués l'un dans l'autre, ce qui est tout à fait autorisé. le
premier bloc teste la valeur de 'DonneReponse'. Si 'DonneReponse' est vraie, un
nouveau bloc if est rencontré : selon que l'utilisateur donne ou pas le bon mot de
passe, le message affiché change. Enfin, si 'DonneReponse' est faux, un message
d'erreur est affiché.
Vous vous étonnez peut-être de voir qu'on a écrit un bloc d'instructions pour le
premier if, et pourtant, une seule instruction (un autre bloc if) y est présente.
Ceci est peu recommandable dans le cas général puisque le begin et le end ne servent
alors à rien. Ils sont bel et bien indispensables dans les premières versions de
Delphi, mais deviennent facultatifs dans Delphi 5. Le bloc if complet devient alors :
if
DonneReponse then
if
Reponse = MotDePasse then
ShowMessage('Mot de passe correct'
)
else
ShowMessage('Mot de passe incorrect'
)
else
ShowMessage('Vous deviez indiquer un mot de passe !'
);
Dans ce cas, il est assez facile de voir que le bloc entier est une seule et unique
instruction : pas un seul point-virgule dans tout le bloc.
Notez enfin qu'il est possible d'enchaîner plusieurs de ces blocs en utilisant la
partie else de l'instruction. Il sera alors possible d'écrire quelque chose
du genre :
if ... then
...
else if ... then
...
else if ... then
...
else ...
Ceci permettra d'envisager successivement plusieurs situations, dont une seule sera
considérée. Il sera possible de donner une situation "poubelle" avec un else final,
qui s'exécutera dans le cas où aucun des autres cas ne se présenterait. Voici un
exemple :
procedure
TForm1.Button1Click(Sender: TObject);
var
Reponse: string
;
begin
Reponse := ''
;
InputQuery('Attention'
, 'oui ou non ?'
, Reponse);
if
(Reponse = 'oui'
) or
(Reponse = 'OUI'
) then
ShowMessage('D''accord, c''est oui !'
)
else
if
(Reponse = 'non'
) or
(Reponse = 'NON'
) then
ShowMessage('Tant pis, c''est non...'
)
else
ShowMessage('Vous pourriez donner votre avis !'
);
end
;
Voici maintenant deux exercices. Ces exercices sont faits pour vous faire écrire tout d'abord un bloc if, puis des fonctions utilisant des blocs if. Aucun d'entre eux ne nécessite d'écrire des blocs d'instructions, des instructions seules sont suffisantes. Vous pourrez cependant compliquer à loisir les exercices si cela vous fait envie. Réussir à résoudre ces exercices n'est pas essentiel mais le fait d'essayer de les résoudre et de bien comprendre la solution proposée est par contre absolument indispensable.
Exercice 1 : (voir la
solution
et les commentaires).
En vous servant de la désormais habituelle procédure 'TForm1.Button1Click', écrivez
les instructions qui permettront de réaliser ce qui suit :
Données du problème :
- la fonction 'sqrt' (« Square Root », « racine carrée » en français) admet un unique paramètre de type 'extended' et renvoie un nombre de type 'extended' qui est la racine carrée du paramètre (Exemple : sqrt(4) donnera 2).
- la fonction 'StrToFloat' (Chaine vers nombre à virgule) admet un unique paramètre de type chaîne, et renvoie un nombre de type 'extended' qui est le nombre représenté par le paramètre (Exemple : StrToFloat('1.2') donnera 1.2).
Fonctionnement souhaité :
- Un nombre est demandé à l'utilisateur (en utilisant 'InputQuery'), on admet ici que c'est toujours un nombre qui sera rentré. Si c'est autre chose qu'un nombre, une erreur se produira, mais ceci ne concerne pas cet exercice et est admis pour le moment.
- Si ce nombre est négatif, un message s'affiche alors, indiquant cela à l'utilisateur, sinon, la procédure affiche la racine carrée de ce nombre (mes excuses en passant à ceux qui n'aiment pas les maths).
Exercice 2 : (voir la
solution et les commentaires).
Dans chacune des questions suivantes, afin de pouvoir vérifier votre travail, il
faudra utiliser la procédure 'TForm1.Button1Click'. Le principe du test consistera à
déclarer et initialiser une variable (en lui donnant une valeur que vous changerez
pour examiner les divers cas possibles), qui sera transmise à la fonction à tester.
Le résultat sera affiché à l'écran comme nous l'avons déjà fait de nombreuses fois.
Chacune des fonctions demandées doit être écrite dans l'unité 'Principale' du projet
'PremierEssai'.
- Ecrire une fonction « NbNegatif » qui prend un nombre comme paramètre. Le résultat de la fonction est une chaîne de caractères dépendant de ce nombre : si le nombre est strictement négatif (non nul), la fonction renvoie « négatif », sinon, elle renvoie « positif ».
- Ecrire une fonction « SigneNombre » qui prend un nombre comme paramètre. Le résultat de la fonction est une chaîne de caractères dépendant de ce nombre : si le nombre est strictement positif, le résultat est « plus », si le nombre est strictement négatif, le résultat est « moins », enfin, si le nombre est 0, le résultat est « zéro ».
Mini-projet n°1
(voir Indications, Solution et Téléchargement)
Vous avez maintenant les compétences requises pour réaliser un premier mini-projet.
Celui-ci consiste en la réalisation d'un petit logiciel que vous connaissez
certainement : la recherche d'un nombre secret. Vous devez créer un nouveau projet
et en faire une application répondant aux exigences ci-dessous :
-
L'interface est, pour cette fois, imposée. Elle doit être similaire à ceci (seuls les textes sont à respecter) :
- Le bouton « Nouveau nombre secret » fixe un nombre secret entier entre 0 et 100. Ce nombre est choisi par l'ordinateur, et n'est ni demandé ni montré à l'utilisateur.
- Le bouton « Proposer un nombre » demande un nombre entier à l'utilisateur. Si le
nombre entier n'est pas compris entre 0 et 100, l'utilisateur en est informé. Sinon,
la position par rapport au nombre secret est indiquée :
« Le nombre secret est inférieur/supérieur à (le nombre proposé par l'utilisateur) » -
Lorsque l'utilisateur trouve le nombre secret, un message l'en informe : « bravo, le nombre secret était (indiquer le nombre secret) », puis le nombre secret est changé et l'utilisateur est informé de ce changement et invité à rejouer : « Nombre secret changé, vous pouvez rejouer ».
Ce mini-projet étant le premier, il est possible que vous ayez du mal à le réaliser. Dans ce cas, n'hésitez pas à consulter les indications, puis le guide pas à pas, ou en dernier recours à télécharger le mini-projet terminé (auquel vous avez accès même si vous avez la version téléchargée du site).
Rappels et indications indispensables :- Pour pouvoir réagir au clic sur un bouton, il faut faire un double-clic dessus dans Delphi, puis écrire les instructions dans la procédure qui est créée à cet effet (Ecrire la procédure et la déclarer sans passer par cette étape du double-clic ne suffit en aucun cas).
VIII-A-2. Blocs 'case'▲
Les blocs case fonctionnent sur un autre principe : elle permet d'examiner la valeur d'une donnée et de décider d'une instruction éventuelle à exécuter suivant les cas. Les blocs case permettront aussi parfois de simplifier des blocs if trop complexes, mais le principe est différent : il s'agit de choisir parmi plusieurs cas possibles et non de prendre une décision comme dans un bloc if. Voici la syntaxe générale d'un bloc case :
case
variable_ordinale of
cas1: instruction1;
[cas2: instruction2;]
{...}
[casn: instructionn;]
[else
instruction;]
end
;
(Ne vous fiez pas à l'aspect un peu barbare de cette syntaxe)
Un bloc case permet d'exécuter au plus une des instructions ou bloc
d'instructions présents dans le bloc (ce qui signifie que si l'un des cas est
réalisé, l'instruction ou bloc d'instructions qui lui correspond sera exécutée, mais
que rien ne sera exécuté si aucun des cas n'est réalisé). Les cas1, cas2 ...
casn permettent de spécifier des valeurs, ou des intervalles de valeurs, ou une
liste de ces derniers séparés par des virgules. Si la valeur de
variable_ordinale est dans l'un de ces cas, l'instruction ou le bloc
d'instructions correspondant est alors exécuté (celle ou celui qui suit
immédiatement l'énoncé du cas). Vous pouvez en outre spécifier un cas
"complémentaire", désigné par else, et qui permet de donner une instruction
ou un bloc d'instruction exécuté si aucun des autres cas n'est réalisé (notez
qu'il n'y a pas de ':' entre else et l'instruction ou le bloc d'instructions
correspondant). Comprenez bien ici que l'instruction présente après else n'est
pas exécutée si un des autres cas est exécuté, mais exécutée dans le cas contraire :
ceci permet de s'assurer que toutes les valeurs possibles pour la donnée seront
couvertes.
Voici un premier exemple d'un bloc case :
function
Random_1_10: Integer
;
begin
Result := Trunc(Random(10
) + 1
);
end
;
procedure
TForm1.Button1Click(Sender: TObject);
var
VTest: Integer
;
begin
VTest := Random_1_10;
case
VTest of
1
: ShowMessage('VTest vaut 1'
);
2
: ShowMessage('VTest vaut 2'
);
7
: ShowMessage('VTest vaut 7'
);
else
ShowMessage('VTest ne vaut ni 1, ni 2, ni 7, mais vaut '
+ IntToStr(VTest));
end
;
end
;
La fonction 'Random_1_10' génère simplement un nombre quelconque entre 1 et 10.
Venons-en à la procédure 'TForm1.Button1Click'. Une variable 'VTest' est déclarée et
initialisée à une valeur entre 1 et 10. Vient ensuite un bloc case qui traite
quelques valeurs (1, 2 et 7) et qui traite les autres dans un cas else.
Chaque cas ne comporte ici qu'une simple instruction, mais on aurait pu mettre à
chaque fois un bloc d'instruction contenant d'autres structures (with et
if, pour ne citer que celles que vous connaissez). Le détail des instructions
est simple pour les trois premiers cas, et utilise 'IntToStr' et une concatènation
pour construire un message dans le dernier cas (cas "complémentaire").
Essayez ce morceau de code sous Delphi : vous verrez que dans 3 cas (1, 2 et 7),
vous avez une fenêtre indiquant la valeur, sinon vous aurez un message indiquant que
la valeur n'est ni 1, ni 2, ni 7, et qui donnera ensuite la valeur de 'VTest'. Par
exemple :
L'utilisation des blocs case est possible, comme nous l'avons vu, avec tous les types ordinaux. Ceci est particulièrement intéressant avec les types énumérés car on peut alors choisir un comportement suivant chacune des constantes d'un de ces types, ou suivant des groupes de valeurs. Examinons un petit exemple :
type
TTypeSupport = (tsInconnu, tsDisq35, tsDisqueDur,
tsCDRom, tsDVD, tsGraveur, tsZIP);
procedure
TestSupport(Supp: TTypeSupport);
begin
case
Supp of
tsDisq35, tsZIP:
ShowMessage('Type de média : Disquettes'
);
tsCDRom, tsDVD, tsGraveur:
ShowMessage('Type de média : Compact-Discs'
);
else
ShowMessage('Type de média : (non amovible)'
);
end
;
end
;
procedure
TForm1.Button1Click(Sender: TObject);
begin
TestSupport(tsDisq35);
end
;
procedure
TForm1.Button2Click(Sender: TObject);
begin
TestSupport(tsGraveur);
end
;
procedure
TForm1.Button3Click(Sender: TObject);
begin
TestSupport(tsDisqueDur);
end
;
(Il est préfèrable de télécharger le code source du projet en cliquant
ici)
Cet exemple montre l'utilisation d'un type énuméré avec une structure case. 3
cas sont envisagés. Les deux premiers donnent une liste de valeurs de type
TTypeSupport (qui est le type du paramètre "Supp" examiné). Dans chacun de ces deux
cas, si la valeur de "Supp" correspond à une des valeurs données, l'instruction
correspondante au cas est exécutée et l'exécution continue après le bloc case
(contrairement à d'autres langages comme le C où il aurait fallu interrompre
l'exécution du bloc au moyen d'une instruction "break;"). Le dernier cas
complémentaire else donne une réaction pour tous les cas non examinés
ailleurs dans le bloc.
Cet exemple utilise trois boutons : chacun lance une fonction identique avec un
paramètre différent pour tester les différents cas. Le bloc case examine la
valeur de "Supp" et décide, suivant que le périphérique accepte les disquettes, les
CD ou rien, d'afficher un message à l'utilisateur. Il est à noter que comme dans
l'exemple précédent, la donnée examinée (à savoir ici le paramètre "Supp") doit être
utilisée comme une constante à l'intérieur du bloc case.
Globalement, les blocs case sont plus rarement utilisés que les blocs
if. Ces derniers blocs sont en effet plus intéressants que les blocs
case. En effet, lorsque vous devrez examiner non pas une condition, mais deux
conditions combinées par un ET logique, les blocs case deviendront
inadéquats.
Vous en savez maintenant assez sur les blocs case, ainsi que sur les blocs
if. Ces structures dites « conditionnelles » sont à la base de la
programmtion en Pascal. Il va maintenant falloir examiner un autre type de bloc
d'instructions : les structures dites « itératives ».
VIII-B. Structures itératives▲
En programmation, il est souvent utile d'exécuter un certain nombre de fois la même instruction ou le même bloc d'instructions en y apportant une légère variation. La répétition controlée permet de résoudre de nombreux problèmes, comme le parcours de tous les éléments d'un tableau par exemple. Imaginons un tableau de 100 éléments :
var
TabTest: array
[1
..100
] of
integer
;
Imaginons maintenant que nous ayons besoin d'ajouter, disons 2 à chacune des 100 valeurs contenues dans ce tableau. Nous pourrions écrire :
TabTest[1
] := TabTest[1
] + 2
;
...
TabTest[100
] := TabTest[100
] + 2
;
Ce qui aurait effectivement pour effet d'ajouter 2 à chaque valeur du tableau. Mais ce genre d'écriture prendrait 100 lignes, du temps, et ne serait pas très passionnante. Imaginons maintenant, pour enfoncer le clou, que 'TabTest' devienne :
var
TabTest: array
[1
..Max_V] of
integer
;
où 'max_v' est une constante. Il sera alors impossible d'écrire les max_v lignes, puisqu'il faudrait supposer la valeur de 'max_v', ce qui empécherait sa modification ultérieure, rendant la constante parfaitement inutile. Pour nous sortir de ce mauvais pas, le langage Pascal met à notre disposition des structures permettant de résoudre le problème de façon très simple. C'est ce qu'on appelle des structures itératives.
VIII-B-1. Blocs 'for'▲
Note aux programmeurs en C ou C++
La boucle 'for' du 'Pascal est nettement moins puissante que celle que vous
connaissez en C (ou en C++). Il est ici hors de question de donner une
condition de sortie pour la variable de contrôle ou de contrôler
l'incrémentation de cette variable. Pour faire cela, il vous faudra employer
les structures 'while' et 'repeat' de Pascal Objet.
La boucle 'for' est certainement la structure itérative la plus simple à comprendre pour un débutant en programmation. Cette boucle permet de répéter une instruction ou un bloc d'instructions en faisant varier la valeur d'une variable entre deux valeurs minimales et maximales. Le bloc a besoin pour fonctionner d'une variable de type ordinal (on utilisera souvent un type 'integer') qui parcourra toutes les valeurs comprises entre les deux valeurs minimales et maximales données. Voici la syntaxe du bloc :
for
variable_ordinale := valeur_minimale to
valeur_maximale do
instruction;
Dans ce bloc, variable_ordinale est une variable de type ordinal, que l'on
appellera « variable de contrôle », valeur_minimale et
valeur_maximale sont deux valeurs de même type ordinal que
variable_ordinale. L'instruction (ou le bloc d'instruction) suivant
le mot réservé do constitue le corps de la boucle : la ou les instructions
qui y sont présentes seront exécutées un nombre de fois égal à
valeur_maximale - valeur_minimale + 1 (dans le cas ou ces valeurs
sont des entiers). Il va de soi que valeur_minimale doit être inférieure
(mais pas forcément strictement) à valeur_maximale. Si ce n'est pas le cas,
l'intérieur du bloc ne sera pas exécuté.
Examinons un premier petit exemple :
procedure
TForm1.Button1Click(Sender: TObject);
var
indx: integer
;
begin
for
indx := 1
to
3
do
ShowMessage('Salut !'
);
end
;
Exécutez ce morceau de code : le message 'Salut !' sera affiché 3 fois à chaque clic sur le bouton. Une variable 'indx' est déclarée et utilisée comme variable de contrôle de la boucle 'for'. Les valeurs minimales et maximales choisies sont respectivement 1 et 3. L'équivalent de
for
indx := 1
to
3
do
ShowMessage('Salut !'
);
est
indx := 1
;
ShowMessage('Salut !'
);
indx := 2
;
ShowMessage('Salut !'
);
indx := 3
;
ShowMessage('Salut !'
);
Mais nous n'utilisons pas ici l'une des possibilités les plus intéressantes de la
boucle 'for' : la variable de contrôle est accessible depuis l'intérieur de la
boucle, mais en tant que constante seulement, il est interdit de la modifier :
comprenez bien ceci, on regarde mais on ne touche pas ! Si vous tentez de modifier
une variable de contrôle à l'intérieur d'une boucle 'for', la compilation devrait
normalement vous signaler une erreur, et si elle ne le fait pas, votre programme a
toutes les chances d'aller s'écraser contre un arbre (virtuel, bien entendu). Pour
simplifier, imaginez que la variable de contrôle doit être utilisée comme un simple
paramètre d'une fonction ou procédure : on peut utiliser sa valeur mais en aucun
cas prétendre à la modifier.
Ces recommandations faites, et, je l'espère, bien comprises, revenons à notre
exemple de tableau de 100 éléments :
var
TabTest: array
[1
..100
] of
integer
;
procedure
TForm1.Button1Click(Sender: TObject);
var
indx: integer
;
begin
for
indx := 1
to
100
do
TabTest[indx] := TabTest[indx] + 2
;
end
;
Cet exemple est des plus importants car il montre quelle utilisation on peut faire
non seulement d'une boucle for, mais également de la variable qui est mise à
jour à chaque itération (chaque exécution du bloc d'instruction qui constitue
l'intérieur de la boucle). La variable inx est ici utilisée comme variable de
contrôle, puis utilisée à l'intérieur de la boucle. Notez tout d'abord qu'en aucun
cas on ne tente de modifier sa valeur? La variable est utilisée pour désigner une
case du tableau. Ainsi, lorsque la variable parcourera les valeurs successives de 1
à 100, les cases successives de 1 à 100 seront modifiées comme désiré, à savoir que
la valeur 2 leur sera ajoutée (il est à noter ici qu'aucune des valeurs contenues
dans le tableau n'a été initialisée, on suppose en effet pour l'exemple qu'une
autre partie du programme s'en est chargé avant).
Il est possible, dans une boucle for, d'utiliser comme valeurs limites
(minimales et maximales) des valeurs de paramètres, de variables, de constantes, de
résultats de fonctions, ou de calculs réalisés à partir de ces derniers. C'est très
pratique lorsqu'on ne connait pas les valeurs limites au moment de l'écriture d'un
programme, il suffira alors de donner la valeur sous forme d'une expression qui
donnera la valeur désirée. Voici un petit exemple théorique pour illustrer cela
:
Essayons maintenant un exemple plus intéressant : le calcul d'une factorielle. Je
rappelle pour le groupe des non-matheux (dont je fais désormais partie :) que la
factorielle d'un nombre entier n (notée « n! ») s'obtient par la
multiplication suivante :
n! = 1 x 2 x 3 x ... x (n - 1) x n
- Ecrivez la fonction for capable de calculer la factorielle d'un entier compris entre 1 et 69. Cette fonction utilisera une boucle for dont la variable de contrôle sera nommée « indx » et le résultat « Fac_n ». L'unique paramètre sera nommé « n » (type entier) et le résultat de type entier. Vous aurez besoin d'utiliser la valeur du paramètre « n » en tant que valeur maximale dans la boucle. Pensez bien à initialiser « Fac_n » avant de débuter le calcul et à filtrer les valeurs de « n » pour n'accepter que celles qui conviennent (la fonction renverra -1 en cas d'erreur). Jetez ensuite un oeil sur la correction donnée ci-dessous :
function
Facto(n: integer
): integer
;
var
indx, Fac_n: integer
;
begin
result := -1
;
if
(n >= 1
) and
(n <= 69
) then
begin
Fac_n := 1
;
for
indx := 2
to
n do
Fac_n := Fac_n * indx;
result := Fac_n;
end
;
end
;
Cette fonction est déjà un peu plus difficile à écrire que toutes les
précédentes. En effet, il s'agit d'inclure le calcul de la factorielle dans un
bloc if qui garantit que le nombre dont on calcule la factorielle est
compris entre 1 et 69 : c'est ce qui est fait ci-dessus. Le calcul proprement
dit est effectué à l'aide d'une boucle for. Le principe est le suivant
: on doit faire une multiplication qu'on ne peut pas poser directement
puisqu'on ne connait pas la valeur de 'n'. On doit pour cela passer par une
décomposition du calcul en petites étapes : des multiplications simples par un
seul nombre. La solution est alors de multiplier successivement par toutes les
valeurs entre 2 (multiplier par 1 n'est pas très intéressant) et la valeur de
'n'. 'Fac_n' est alors initialisé à 1, puis multiplié par les valeurs
successives de 'indx' entre 2 et n, ce qui donne bien la factorielle de n. Ne
surtout pas oublier à la fin d'un tel calcul de fixer le résultat de la
fonction à la valeur calculée (Note importante : nous aurions pu tout aussi
bien utiliser directement 'result' en lieu et place de 'Fac_n' dans la fonction,
mais j'ai délibérément évité pour ne pas trop vite embrouiller les choses ;=).
C'est déjà tout pour ce paragraphe consacré aux boucles for. Les exemples
d'utilisation de ces boucles sont innombrables et vous aurez l'occasion d'en voir
certain au cours des mini-projets qui suivront dans le guide. Sachez cependant
qu'en programmation, l'utilisation des boucles for est moins répandu qu'on
ne pourrait croire, car elles imposent un nombre d'itérations (de passages) au
moins virtuellement connu (Valeur_maximale - Valeur_minimale + 1). Il
est souvent utile de répèter une instruction ou un bloc d'instructions jusqu'à ce
qu'une condition soit remplie : c'est le rôle des deux structures while et
repeat.
VIII-B-2. Blocs 'while'▲
Les blocs while, qu'on appelera aussi boucle while, sont plus
puissants mais plus délicates à manipuler que les boucles for, tout en étant
réservées à d'autres usages. Le principe d'un bloc while est de tester une
valeur booléenne et d'exécuter une instruction ou un bloc d'instruction si elle est
vraie. La boucle se termine dés que la valeur booléenne est fausse (l'instruction
ou le bloc d'instructions qui constitue le corps de la boucle peut ne pas être
exécuté du tout).
Voici la syntaxe de ce bloc :
while
condition_booleenne do
instruction; { ou bloc d'instruction }
Voici maintenant le schéma de fonctionnement d'une boucle while :
La condition booléenne employée dans ce bloc peut être n'importe quelle expression
dont le type est booléen : un booléen seul, une ou des opérations logiques entre
booléens, des comparaisons de valeurs, des résultats de fonctions, des
paramètres... Tout est permis du moment que l'ensemble forme un booléen. Nous
aurons l'occasion de travailler cette partie au fur et à mesure de la progression
dans le guide car les boucles while sont très largement employées en
Pascal.
Le test sur la condition effectué, deux actions sont possibles : si le booléen est
faux (false), alors le bloc se termine, sinon, si le booléen est vrai (true), alors
l'instruction ou le bloc d'instruction est exécuté. A la fin de l'exécution de
cette instruction ou de ce bloc d'instructions, la condition booléenne est évaluée
à nouveau et ainsi de suite. Une conséquence immédiate à comprendre est que
contrairement aux boucles for où la variable de contrôle devait être
manipulée comme une constante, il faudra ici faire bien attention à modifier le
résultat de la condition booléenne à l'intérieur de la boucle, ou sinon, on se
retrouvera avec une boucle infinie qui plantera l'ordinateur.
Voici un exemple de ce qu'il ne faut pas faire :
while
i > 0
do
a := a + 2
;
Vous voyez où est l'erreur ? Le problème consiste en le fait que la condition (i >
0) sera évaluée une fois : si elle est fausse, alors tout ira bien, sinon,
l'instruction s'exécutera, ne modifiant pas i. La valeur de i n'étant pas modifiée,
la condition sur i sera encore vraie, ce qui boucle un cycle sans fin, entrainant
un blocage du programme et probablement un plantage de l'ordinateur.
Mais que cela ne vous empêche pas d'utiliser les boucles while ! Elles sont
très utiles et souvent indispensables. Voici le même exemple, mieux écrit :
i := 10
;
while
i > 0
do
begin
a := a + 2
;
i := i - 1
;
end
;
Dans l'exemple ci-dessus, on ne se lance pas tête baissée dans l'exécution de
l'instruction (a := a + 2). On commence par initialiser i, et on prend bien soin de
le modifier à l'intérieur de la boucle : l'effet ici sera d'enlever 1 à i à chaque
itération de la boucle, ce qui aura pour conséquence de faire prendre la valeur 0 à
i au bout de 10 itérations, arrètant là l'exécution de la boucle. Nous avons en
fait recréé ici l'équivalent d'une boucle for avec une boucle while,
ce qui montre bien que cette dernière est plus puissante.
Ce dernier exemple n'utilise pas le principal intérêt des boucles while, car
on peut encore prévoir dans cet exemple le nombre d'itérations. Considérons un
autre exemple plus pertinent :
procedure
TForm1.Button1Click(Sender: TObject);
var
Reponse: string
;
const
RepQuit = 'q'
;
begin
Reponse := ''
;
while
Reponse <> RepQuit do
begin
InputQuery(''
, 'quelque chose à dire ?'
, Reponse);
if
Reponse <> RepQuit then
ShowMessage(Reponse);
end
;
end
;
Dans cet exemple, un clic sur le bouton initialise 'Reponse' à la chaine vide, puis
lance une boucle while. Cette boucle s'arrète à la condition que la valeur
de 'Reponse' soit la chaîne « q ». Ce qui se passe dans la boucle est assez simple
: on demande à l'utilisateur de taper quelque chose, que l'on stocke justement dans
'Reponse', puis on affiche ce que l'utilisateur vient de taper. Le seul moyen de
quitter la boucle est de taper la chaîne « q ». Le bloc if permet d'éviter
que cette chaîne soit répètée à l'utilisateur. La boucle se termine alors car la
condition 'Reponse <> RepQuit' devient fausse.
Comme pour les boucles 'for', d'autres exemples viendront en leur temps au fur et à
mesure du guide.
Nous allons maintenant nous attaquer à une manipulation guidée pas à pas : suivez
les indications ci-dessous sous Delphi pour suivre les explications (si vous
n'arrivez pas à réaliser toutes ces opérations, n'ayez pas peur : vous pouvez
télécharger le projet créé pendant la manipulation ci-dessous) :
L'objectif de cette manipulation est de recréer le projet « Nombre secret » d'une
manière plus rapide et plus agréable autant pour nous que pour l'utilisateur. Je
préfère vous prévenir : cette manipulation va peut-être vous paraître difficile.
L'essentiel ici est de comprendre l'utilisation de la boucle while et de
l'utilité des imbrications de blocs que nous allons construire.
- Créez un nouveau projet (que vous enregistrerez immédiatement, ainsi que la seule unité et sa fiche associée, en trouvant vous-même des noms adéquats), placez un unique bouton sur la fiche principale, et dimensionnez tout cela à votre goût. Saisissez comme texte du bouton : « Jouer ! » (Pour ceux qui ne se rappeleraient plus comment on fait, il faut sélectionner le bouton à l'aide d'un simple clic, puis aller dans l'inspecteur d'objets, dans la page 'Propriétés' ('Properties' en anglais), et modifier la ligne intitulée 'Caption'). De la même manière, changez le titre de la fenêtre à « Le nombre secret ».
- Créez la procédure répondant à un clic sur le bouton (en effectuant un double-clic sur ce dernier). Le principe va être ici de tout faire en une seule procédure : la première étape sera de générer un nombre secret, la deuxième de lancer la phase interactive avec l'utilisateur (la seule qu'il verra), tout en pensant à garder à cet utilisateur une porte de sortie. Nous aurons besoin pour écrire cette procédure d'une boucle while et d'un gros bloc if.
-
Le nombre secret sera stocké dans une variable interne nommée 'NbSec'. Déclarez cette variable de type integer :
Sélectionnezvar
NbSec:integer
;puis écrivez l'instruction qui génèrera un nombre secret entre 0 (inclus) et 100 (inclus) :
SélectionnezNbSec := Trunc(Random(
101
)); -
Comme dans tout programme où l'utilisateur intervient, il va nous falloir deux variables de plus : 'Reponse' de type chaîne de caractères et 'NbPropose' de type integer. Déclarez ces deux variables. Le bloc var de votre procédure doit ressembler à cela :
Sélectionnezvar
NbSec, NbPropose:integer
; Reponse:string
; -
N'oublions pas d'initialiser 'Reponse', cela nous servira à entrer dans la boucle que nous allons construire. Initialisez donc la variable 'Reponse' à la chaîne vide :
SélectionnezReponse :=
''
; -
La boucle s'arrètera lorsque l'utilisateur entrera « q ». Créez une constante contenant ce caractère sous le nom « RepQuit » :
Sélectionnezconst
RepQuit ='q'
; -
Ecrivons maintenant le squelette de la boucle. La condition booléenne est des plus simples.
Sélectionnezwhile
Reponse <> RepQuitdo
begin
end
; -
Nous allons maintenant réfléchir en terme d'itération : ce que nous allons écrire à l'intérieur de la boucle sera probablement exécuté plusieurs fois de suite. Il faudra faire en sorte que tout soit règlé à la perfection et que l'utilisateur ne voit rien de ce que nous ferons. La première chose consiste donc à lui demander une proposition. Pour cela, un appel de 'InputQuery' est nécessaire :
SélectionnezInputQuery(
'Proposition d''un nombre'
,'Veuillez indiquer une proposition (''q'' pour arrèter)'
, Reponse); -
Il va maintenant falloir faire quelque chose de cette réponse : on ne sait pas si l'utilisateur désire quitter ('q'), ou a proposé un nombre. Pour simplifier, nous admettrons que toute réponse différente de 'q' est une proposition de nombre. Mais revenons à notre problème : deux cas sont possibles, il va donc falloir construire un bloc if et ceci à l'intérieur du bloc while déjà entamé (rappelez-vous bien que l'art de bien programmer passe par l'art de bien combiner les différentes structures élémentaires dont vous disposez). Voici le squelette du bloc if à écrire :
Sélectionnezif
Reponse = RepQuitthen
begin
end
else
begin
end
; -
Occupons-nous du cas où l'utilisateur a entré 'q' : un message l'informant qu'il a perdu, en lui donnant la valeur du nombre secret, est ce qui paraît le plus adapté. Entrez donc dans le premier bloc d'instruction (celui qui suit juste le then) l'instruction :
SélectionnezShowMessage(
'Vous avez perdu, le nombre secret était '
+ IntToStr(NbSec));Il est inutile d'en faire plus : nous n'écrirons plus rien après le bloc if, et à la prochaine itération, la condition (Reponse <> RepQuit) n'étant plus vérifiée, la boucle se terminera, terminant également la procédure puisque nous n'écrirons rien après la boucle while. Important : Vous retrouverez souvent cette technique qui consiste à isoler quelques instructions comme les initialisations au début d'une procédure, puis à lancer une boucle ou une condition, et ainsi de suite : ceci permet en quelque sorte de « protéger » des instructions de plusieurs cas nuisibles. Ici, nous éliminons le cas où l'utilisateur désire quitter et nous allons maintenant nous concentrer sur le cas où il a fait une proposition.
-
Il nous reste à écrire un bloc d'instructions : celui qui suit le else. Dans ce cas, on a une proposition de l'utilisateur, à laquelle on doit répondre. Commencez par écrire l'instruction qui convertira la réponse en entier :
SélectionnezNbPropose := StrToInt(Reponse);
Pour vous donner un point de repère, voici le code source de la procédure tel qu'il devrait être écrit à la fin de cette étape :
Sélectionnezprocedure
TForm1.Button1Click(Sender: TObject);const
RepQuit ='q'
;var
NbSec, NbPropose:integer
; Reponse:string
;begin
NbSec := Trunc(Random(101
)); Reponse :=''
;while
Reponse <> RepQuitdo
begin
InputQuery('Proposition d''un nombre'
,'Veuillez indiquer une proposition (''q'' pour arrèter)'
, Reponse);if
Reponse = RepQuitthen
begin
ShowMessage('Vous avez perdu, le nombre secret était '
+ IntToStr(NbSec));end
else
begin
NbPropose := StrToInt(Reponse);{ comparaison au nombre secret }
end
;{if}
end
;{while}
end
;Prenez au début cette sage habitude qui consiste à décrire en commentaires chaque end de fin de bloc, ou vous finirez rapidement comme moi au début par vous perdre dans vos instructions lorsqu'il faudra fermer 5 ou 6 end à la suite !
-
Voilà, nous pouvons maintenant comparer cette proposition et le nombre secret. Mais diable, il va encore falloir distinguer plusieurs cas !!! (Ne vous affolez surtout pas, j'ai un peu fait exprès d'imbriquer plusieurs blocs pour vous montrer les complications que cela peut rapidement apporter. Pour vous aider, rappelez-vous bien qu'à l'intérieur d'un bloc, vous n'avez absolument pas à vous soucier de l'extérieur : concentrez-vous sur l'écriture du bloc en lui-même avant de l'intègrer au programme)
L'instruction qui va suivre la conversion de 'Reponse' en entier est donc un bloc if. Ce bloc, il va falloir le construire, mais là, nous avons de l'avance puisque nous avons déjà écrit quelque chose de similaire. Le bloc en question va être écrit là ou est placé le commentaire { comparaison au nombre secret }. Voici le bloc qu'il vous faut écrire :Sélectionnezif
(NbPropose <0
)or
(NbPropose >100
)then
ShowMessage('Le nombre secret est compris entre 0 et 100'
)else
if
NbPropose < NbSecthen
ShowMessage('Le nombre secret est supérieur à '
+ IntToStr(NbPropose))else
if
NbPropose > NbSecthen
ShowMessage('Le nombre secret est inférieur à '
+ IntToStr(NbPropose))else
begin
ShowMessage('bravo, le nombre secret était '
+ IntToStr(NbPropose)); Reponse := RepQuit;end
;Voici les explications de ce bloc : le premier cas traite les valeurs non acceptées : en dessous de 0 ou au dessus de 100. Les deuxième et troisième cas traitent les cas où le nombre secret est différent du nombre proposé, mais où ce dernier est entre 0 et 100. Le dernier cas s'occupe du cas où le nombre secret est trouvé. Vous vous interrogez peut-être sur l'instruction 'Reponse := RepQuit'. vous avez raison de vous poser la question, mais réfléchissez bien : après avoir informé l'utilisateur qu'il a gagné, il faut arrèter la boucle. On pourrait utiliser un moyen que vous ne connaissez pas encore, à savoir 'break', mais il est plus commode de s'assurer que la prochaine itération n'aura pas lieu en donnant à 'Reponse' la valeur qui termine justement la boucle.
- C'est terminé ! Si vous avez perdu le fil, téléchargez le projet terminé, et reprenez la manipulation à loisir. J'ai choisi volontairement un exercice difficile pour vous, l'objectif n'étant pas que vous soyez capable de reproduire ce que nous venons de faire, mais que vous compreniez bien l'intérêt qu'il y a à imbriquer les blocs les uns dans les autres. Remarquez au passage que la procédure que nous venons d'écrire est d'une longueur remarquable. C'est promis, plus d'exercices aussi tordus avant un petit moment.
VIII-B-3. Blocs 'repeat'▲
Les blocs repeat sont une variante des blocs while. Certains
connaisseurs vont peut-être bondir en lisant cette phrase, mais je maintiens que le
principe de base est le même, mais pour des emplois différents. Alors qu'une boucle
while permet de répèter un bloc d'instruction tant qu'une condition est
satisfaite, un bloc repeat permet de répèter un bloc d'instructions tant
qu'une condition (toujours booléenne) n'est pas remplie : cette condition sera
nommée « condition de sortie » de la boucle. Une autre différence est que le bloc
while peut ne pas exécuter les instructions contenues dans le bloc, alors
qu'un bloc repeat exécutera toujours au moins une fois les instructions
contenues dans le bloc. La condition de sortie est testée après chaque itération,
alors que dans un bloc while, c'est avant chaque itération.
Pour clarifier les choses, et avant même de connaître la syntaxe des blocs
repeat, voici un tableau qui regroupe les différences des deux structures de
bloc while et repeat :
Type de bloc : | Sortie du bloc : | Nombre minimum d'itérations : |
---|---|---|
while | condition fausse | 0 |
repeat | condition vraie | 1 |
Voici maintenant la structure de ce bloc :
repeat
instruction_1;
...
instruction_n;
until
condition_de_sortie;
Le bloc commence par le mot Pascal réservé repeat (qui a donné son nom au
bloc) et se termine par le mot until suivi d'une condition booléenne de
sortie. Entre ces deux mots réservés, peuvent être écrites autant d'instructions
que l'on veut : c'est une particularité de ce bloc, qui est à lui seul un bloc
d'instructions (le begin et le end sont inutiles (donc interdits) et
remplacés par les mots repeat et until. Vous comprendrez mieux cela
en regardant les exemples donnés ci-dessous.
Revenons un instant sur la condition booléenne. Cette condition est la condition
qui doit être respectée pour qu'une autre itération soit lancée. Comprenez bien que
la première itération s'effectuera toujours, puis ensuite seulement la condition de
sortie est vérifiée. Si cette condition est vraie, la boucle se termine, si
elle est fausse, une autre itération est lancée avec vérification de la condition
en fin d'itération, et ainsi de suite.
Voici également, comme pour le bloc while, le schémas de fonctionnement d'un
bloc repeat :
Examinez ce schémas à loisir et comparez-le à celui des boucles while : vous
remarquerez que les instructions sont exécutées avant le test de la condition
booléenne dans le cas des boucles repeat. Remarquez également que ce n'est
pas la même valeur pour cette condition qui décide de la sortie de la boucle.
Tout comme pour une boucle while, il faudra faire en sorte de ne pas créer
une boucle infinie. Pour cela, faire en sorte que la condition de sortie soit
réalisée est indispensable à un endroit donné dans la boucle (mais ce changement
peut ne pas être explicite et dépendre d'un paramètre indépendant de vous, comme
atteindre la fin d'un fichier par exemple.
Voici comme premier exemple le petit programme perroquet déjà construit avec une
boucle while au paragraphe précédent : il a été réécrit ici en utilisant une
boucle repeat, plus adaptée ici :
procedure
TForm1.Button1Click(Sender: TObject);
var
Reponse: string
;
const
RepQuit = 'q'
;
begin
Reponse := ''
;
repeat
InputQuery(''
, 'quelque chose à dire ?'
, Reponse);
if
Reponse <> RepQuit then
ShowMessage(Reponse);
until
Reponse = RepQuit;
end
;
Vous verrez si vous comparez les deux versions de la procédure que les différences
sont assez mineures : on a changé de type de boucle, ce qui ne change en rien ni
les déclarations ni l'initialisation de 'Reponse'. La condition 'Reponse <>
RepQuit' est déplacée en fin de boucle pour satisfaire la syntaxe de la boucle
repeat, mais c'est sa négation logique que l'on prend, car on doit non plus
donner une condition pour continuer, mais pour ne pas continuer, c'est-à-dire
arrèter. La négation logique de 'Reponse <> RepQuit' est 'not (Reponse <>
RepQuit)', ou plus simplement 'Reponse = RepQuit'. Les deux instructions présentes
à l'intérieur de la boucle sont inchangées.
Pourquoi, me direz-vous, perdre mon temps à vous débiter un exemple déjà traité,
alors que je pourrais vous en trouver un autre ? C'est tout simplement car du point
de vue logique, la deuxième version (en utilisant une boucle repeat) est
plus correcte que la première. En effet, le principe du programme est de répèter
tout ce que dit l'utilisateur, et il faudra donc bien pour cela lui poser une
première fois la question (l'appel à InputQuery) : du point de vue théorique, la
boucle doit être exécutée au moins une fois : c'est la boucle repeat qui
s'impose alors.
Dans vos programmes, il vous faudra également réfléchir de cette manière : une
itération est-elle indispensable ou non ? Si la réponse est oui, il vous faudra
utiliser une boucle repeat, et une boucle while dans l'autre
cas. La condition booléenne ne pose pas de problème car la condition d'arrèt
utilisée dans une boucle repeat est habituellement la négation de la
condition qui apparaitrait dans une boucle while (condition de « non arrèt
»).
Les exemples utilisant des boucles while n'étant pas faciles à trouver,
d'autres viendront dans la suite du guide ou dans ce paragraphe si j'en trouve
d'intéresants.
VIII-B-4. Contrôle avancé des boucles▲
Lorsqu'on programme en utilisant des boucles, il se présente des situations où l'on aurait besoin de sauter la fin d'une itératon, de sortir immédiatement de la boucle en cours, ou même de terminer la procédure ou la fonction en cours d'exécution. Dans ces trois situations, on a besoin de « casser » le rythme normal de l'exécution du programme. Pour chacun de ces besoins, le langage Pascal a prévu une instruction spécifique. Les trois instructions correspondantes seront :
- Continue;
- Break;
- Exit;
Les deux premières instructions (Continue et Break) ne doivent être présentes qu'à
l'intérieur d'une boucle : leur présence en dehors est interdite. La troisième peut
être présente n'importe où à l'intérieur d'une procédure ou d'une fonction. Il est
à noter que ces trois éléments sont des appels de procédures très spéciales sans
paramètres et non pas des mots réservés Pascal.
L'instruction 'Continue;', lorsqu'elle est exécutée, termine immédiatement
l'itération qui vient d'être entamée, et passe à la suivante : comprenez bien
qu'elle ne termine pas la boucle (pour cela, utilisez 'Break;'). Cette instruction
équivaut à la rencontre de la fin du bloc d'instructions pour les boucles
for et while, et à la rencontre du mot until pour les boucles
repeat.
Voici un exemple simple, dont le code source sera probablement exploré plus tard
dans le guide (car encore un peu trop difficile actuellement) : quelles lettres de
lecteurs sont disponibles sur votre ordinateur ? Si vous avez connu Windows 3.1,
vous vous rappelez certainement qu'on devait choisir dans une liste déroulante ne
présentant que les lecteurs valides. Cette liste, que vous pourrez utiliser avec
Delphi (le composant s'appelle 'DriveComboBox') est construite au moyen d'une
boucle for. Par un moyen que vous n'avez pas encore à connaître, la liste
des lettres disponibles est obtenue (c'est Windows qui donne ces informations).
Pour chaque lettre, une vérification est faite : si elle est disponible, elle est
ajoutée à la liste, sinon, l'itération est stoppée au moyen de 'Continue'.
L'instruction 'Break;', quant à elle, termine complètement la boucle dans laquelle
elle est présente. Souvenez-vous de la manipulation ci-dessus : on a à un moment
donné fixé la valeur de la variable 'Reponse' à 'RepQuit' pour s'assurer de quitter
la boucle. Imaginons que la boucle s'allonge d'une centaine de lignes (ca arrive
parfois...), que pour une raison X ou Y, on vienne à modifier la condition, et pas
l'instruction qui est censée provoquer la sortie de boucle : panique garantie ! La
solution consiste à remplacer cette affectation de fortune par l'instruction
'Break;', qui terminera immédiatement la boucle et passera à la suite.
L'instruction 'Exit;', enfin, constitue le siège éjectable d'une procédure ou
fonction. Cette manière de quitter une procédure est décriée par de nombreuses
personnes, pretextant qu'il ne s'agit là que d'une manière commode de pallier à un
algorithme défectueux. N'écoutez ces récriminations que d'une seule oreille, car
vous auriez bien tort de vous priver d'utiliser 'Exit;' (après tout, le code source
livré avec Delphi comporte de nombreux appels à 'Exit;'). 'Exit;' permet en fait
d'éliminer rapidement des cas indésirables dés le début d'une procédure ou d'une
fonction. Encore une fois, il va falloir patentier un peu pour les exemples, mais
je peux vous assurer qu'ils viendront (Mini-projet à la fin du chapitre 8 par
exemple).
Plus généralement, l'ensemble de ce chapitre constitue une base pour tout ce qui va
suivre. Les structures et les instructions vues ici seront abondamment employées
dans vos programmes, et vous apprendrez à les maîtriser au fur et à mesure de leur
utilisation. En guide de conclusion de chapitre, il est important pour vous
connaître, mais pas forcément de bien maîtriser chacune des structures vues dans ce
chapitre. Vous aurez tout le temps de découvrir l'utilité de chacune de ces
structures quand le besoin apparaîtra.
A ce stade du guide, vous commencez à avoir une connaissance appréciable du langage
Pascal, même si quelques domaines sont encore inexplorés, comme les pointeurs ou
les objets. Ce chapitre-ci est terminé (son écriture m'aura pris trois semaines,
pour un résultat à mon sens assez moyen).
Avez-vous remarqué que nous sommes pour l'instant réduits, sous Delphi, à
n'utiliser que bien peu de choses ? C'était nécessaire tant que vous ne connaissiez
pas assez le langage Pascal, ce qui n'est plus le cas. Le prochain chapitre sera
consacré bien plus à Delphi qu'au langage, même si des notions très importantes de
ce dernier y seront vues. L'objectif en sera ambitieux : la connaissance de
quelques composants, de leur utilisation, de leurs propriétés et de leurs
évènements.