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.
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 :
|
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) :
Voici comme d'habitude un exemple utilisable sous Delphi (toujours au même endroit) :
|
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 :
Cet exemple manquant un peu de piment, nous allons un peu le compliquer :
|
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 :
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 :
|
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 :
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 :
|
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 :
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') :
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 :
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 :
|
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 :
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 : (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 :
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'.
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 :
(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 :
|
(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 :
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 :
|
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 :
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 :
|
(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 ».
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 :
|
Imaginons maintenant que nous ayons besoin d'ajouter, disons 2 à chacune des 100 valeurs contenues dans ce tableau. Nous pourrions écrire :
|
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 :
|
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 :
|
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 :
Examinons un premier petit exemple :
|
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
|
est
|
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 :
Ces recommandations faites, et, je l'espère, bien comprises, revenons à notre exemple de tableau de 100 éléments :
|
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
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 :
|
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.
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 :
Voici la syntaxe de ce bloc :
|
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 :
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 :
|
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 :
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 :
|
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 :
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 :
|
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.
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 :
var
NbSec:integer
;puis écrivez l'instruction qui génèrera un nombre secret entre 0 (inclus) et 100 (inclus) :NbSec := 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 :
var
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 :
Reponse :=
'
'
; - La boucle s'arrètera lorsque l'utilisateur entrera « q ». Créez une constante contenant ce caractère sous le nom « RepQuit » :
const
RepQuit ='
q
'
; - Ecrivons maintenant le squelette de la boucle. La condition booléenne est des plus simples.
while
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 :
InputQuery(
'
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 :
if
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 :
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.
- 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 :
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 <> 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 :if
(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 :
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 :
|
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 :
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 :
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 :
|
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.
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.
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.
Aucun commentaire:
Enregistrer un commentaire