Qlqs Procedure Delphi 7
Qlqs Procedure Delphi 7
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, ...
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.
if valeur_booleenne then
instruction;
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 :
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).
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)
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') :
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 :
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 :
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 :
Exercice 2 :
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'.
Mini-projet n°1
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 :
1. L'interface est, pour cette fois, imposée. Elle doit être similaire à ceci (seuls
les textes sont à respecter) :
2. 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.
3. 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) »
o 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).
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;
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.
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);
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 ».
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'▲
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 :
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é.
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;
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
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.
while condition_booleenne do
instruction; { ou bloc d'instruction }
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.
end;
8. 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 :
InputQuery('Proposition d''un nombre', 'Veuillez indiquer une
proposition (''q'' pour arrèter)', Reponse);
9. 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 :
if Reponse = RepQuit then
begin
end
else
begin
end;
[Link]-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 :
ShowMessage('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.
[Link] 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 :
NbPropose := 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 :
procedure TForm1.Button1Click(Sender: TObject);
const
RepQuit = 'q';
var
NbSec, NbPropose: integer;
Reponse: string;
begin
NbSec := Trunc(Random(101));
Reponse := '';
while Reponse <> RepQuit do
begin
InputQuery('Proposition d''un nombre',
'Veuillez indiquer une proposition (''q'' pour arrèter)', Reponse);
if Reponse = RepQuit then
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 !
[Link]à, 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 :
if (NbPropose < 0) or (NbPropose > 100) then
ShowMessage('Le nombre secret est compris entre 0 et 100')
else if NbPropose < NbSec then
ShowMessage('Le nombre secret est supérieur à ' +
IntToStr(NbPropose))
else if NbPropose > NbSec then
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.
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 :
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.
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.