Accueil        Lexique        Unités        Algorithmes        TUTORIEL        Groupe Facebook   

Sommaire Tutoriel

Les instructions de boucle

En programmation, il arrive très souvent que vous deviez effectuer des opérations en boucle. Par exemple, faire un traitement sur tous les pixels d'une image. Le langage Pascal propose 3 instructions de boucle ayant leur particularité.

L'instruction For fait varier une variable de type ordinale (Entier ou char) de manière croissante ou décroissante

L'instruction While est une boucle qui s'exécute tant que les conditions de la boucle sont remplies : Cette boucle peut s'arrêter dès le début.

L'instruction Repeat fait la même chose que While à part que la boucle s'exécute au moins une fois avant de rencontrer les conditions.

  1. Boucle For
  2. Boucle While
  3. Boucle Repeat
  4. Instruction GOTO
  5. Notions vues dans ce cours

  1. Boucle FOR

    Structure d'une boucle For

    La structure d'une boucle FOR est définie comme suit :
    Var
        i : word;
        c : char;
    Begin
        for i:=1 to 10 do
            writeln(i);
        for c:='Z' downto 'A' DO
            writeln(c);
        readln;
    End.
    
    En Pascal, la boucle FOR ne peut être utilisée que pour une seule variable de type ordinale (Entière ou char), cette variable étant incrémentée de 1 ("TO") ou décrémentée de 1 ("DOWNTO") à chaque fin de boucle tant que cette variable dedépasse pas la valeur maxi (TO) ou mini (DONWTO). Ainsi, si vous tentez d'exécuter le code suivant :
    var
        r:real;
    begin
        for r:=1 to 10 do;
        readln;
    end.
    
    à la compilation, un message d'erreur vous indiquera que les boucles FOR ne peuvent être utilisées qu'avec des variables de type ordinal (entière ou char). En langage machine (dans les microprocesseurs), la boucle FOR utilise des instructions permettant l'incrémentation et la décrémentation de régistres d'index. Malheureusement, la boucle FOR ne fait qu'une simple incrémentation ou décrémentation alors que dans certains microprocesseurs, comme le 6809, les index peuvent être doublement incrémentés ou décrémentés (X++, Y-- etc).

    Contrairement au Basic, il n'y a pas de possibilité de changer la taille du pas, et contrairement au langage C (et Java, PHP etc), il n'y a pas possibilité de mettre plusieurs conditions de sortie de boucle avec FOR. Pour cela, utilisez l'instruction WHILE ou REPEAT décrit après dans ce chapitre.

    Dans une boucle for, l'index NE PEUT PAS changer de valeurs en dehors de ce que le programme Pascal fait (incrémentation ou décrémentation). Ainsi si vous essayez d'exécuter le code suivant :

    {...}
        for i:=1 to 10 do
            if i=5 then
                i:= 2*i;
    {...}
    
    cela entrainera une erreur indiquant qu'il est impossible de changer la valeur de l'index d'une boucle FOR ! Le langage Pascal est d'une nature beaucoup moins permissive que le langage C ce qui diminue considérablement les bugs de logiciels une fois compilé.

    Exemple : somme des N premiers entiers

    Soit le programme suivant
    Program boucle1a;
    Var
        i,n : word;
        r:LongInt;
    Begin
        r:=0;
        write ('Valeur de N ? (<65535) ');
        readln(n);
        writeln ('le calcul par méthode itérative se fait...');
        for i:=1 to N do
            r:= r + i;
        writeln('La somme des ',n
                ,' premiers entiers (méthode itérative) est : '#10#13,
                r);
        readln;
    End.
    
  2. Ce programme fournira la somme des N premiers entiers naturel. Testez ce programme avec différentes valeurs de N. Tant que N est inférieur ou égal à 65534, le calcul se fera et donnera un résultat correct pour r, tout en affichant la valeur saisie pour N. Mais : Bien qu'en soit, ce programme suffit à répondre à nos besoins, on voudrait pouvoir aller plus loin dans N (par exemple, jusqu'à au moins les 100 millions) et pas se limiter à 65536. Pour cela, on va changer les types de I et N en LongInt et R en real. Voici le même programme modifié avec les types :
    Program boucle1b;
    Var
        i,n : LongInt;
        r:Real;
    Begin
        r:=0;
        write ('Valeur de N ? (< 2 milliards) ');
        readln(n);
        writeln ('le calcul par méthode itérative se fait...');
        for i:=1 to N do
            r:= r + i;
        writeln('La somme des ',n
                ,' premiers entiers (méthode itérative) est : '#10#13,
                r:10:0);
        readln;
    End.
    
    Dans ce programme, nous affichons le résultat réel sous forme entier avec au moins 10 caractères (rempli par desblancs à gauche au besoin).
    En utilisant ces types LongInt (Entiers long signés, limité à 2 milliards de valeurs environ) et Real (Réel de 10 octets dont 8 sont significatifs, soit 18 à 20 chiffres significatifs) nous pouvons avoir des nombres assez grands. Cependant avec des grands nombres, le temps de calcul peut dépasser les 5 secondes : Or en mathématiques on démontre que la somme des N premiers entiers vaut n*(n+1) / 2 (on remarque au passage que soit n, soit n-1 est pair, le résultat est donc obligatoirement entier). On va ajouter une portion de code donnant le résultat avec cette même formule.
    Program boucle1c;
    Var
        i,n : LongInt;
        r:Real;
    Begin
        r:=0;
        write ('Valeur de N ? (< 2 milliards) ');
        readln(n);
        writeln ('le calcul par méthode itérative se fait...');
        for i:=1 to N do
            r:= r + i;
        writeln('La somme des ',n
                ,' premiers entiers (méthode itérative) est : '#10#13,
                r:10:0);
        writeln ('Appuyez sur Entrée pour continuer');
        readln;
        writeln ('le calcul par méthode rapide se fait...');
        r:= n/2 * (n+1);
        writeln('La somme des ',n
                ,' premiers entiers (méthode rapide) est : '#10#13,
                r:10:0);
        readln;
    End.
    
    Pour que le résultat de la méthode rapide soit juste, notamment pour les grandes valeurs, il est OBLIGATOIRE que le compilateur Pascal fasse la conversion en réel dès le début afin de ne pas avoir d'erreur sur le résultat souhaité : c'est ce que permet la première division sur la valeur entière.
    Il est important de connaître la façon dont le compilateur Pascal agit sur les conversions de type avec les opérations. Si au lieu de r:=n /2*(n+1);,vous aviez écrit r:=n*(n+1)/2; le résultat aurait été faux pour des grandes valeurs de N (au delà de 65536), ou cela aurait entraîné une erreur pendant l'exécution.
    Si vous n'avez pas la possibilité d'effectuer une opération de conversion en réel dès le début, utilisez une valeur réelle comme 0.0 (en cas de somme ou différence) ou 1.0 (en cas de produit ou division) : ainsi, pour notre code, vous auriez pu écrire simplement r:= 1.0 * n*(n+1)/2; ou r:= 0.0 + n*(n+1)/2; qui aurait effectué la conversion en réel dès le début.

    Important : pour la méthode rapide, le type du résultat, dans ce cas particulier doit posséder le double d'octets de la valeur initiale puisque le résultat doit pouvoir contenir au moins le carré de la valeur maximale de N (231). Il est important d'analyser les valeurs limites des opérations mathématiques avant de choisir les types adéquats des variables.

    La recherche d'optimisation algorithmique est un travail important des développeurs informatique, en réduisant la Complexité des algorithmes. Plus un algorithme utilise de boucles et plus il est d'une grande complexité. Ici N est passé de la complexité O(N) à O(1)... O(N) signifie que la complexité est proportionnelle à N et O(1), qu'elle ne demande qu'une opération.

    Exercices


  3. Boucle While

    La boucle While est utilisée pour effectuer un même ensemble d'instructions Tant que les conditions initiales sont satisfaites.

    Exemple 1

    var 
        i : word;
    begin
        i:=1;
        while (i mod 7 <> 0) and (i mod 13 <> 0) do
            inc(i);
        writeln('Le premier nombre divisible par 7 ',
                'et par 13 est : 'i);
        readln
    end.
    
    Ce programme donnera le premier nombre à la fois divisible par 7 et par 13.

    La fonction inc() est une des nombreuses fonctions système de Pascal. Cette fonction incrémente la variable donnée en argument. La présence d'un 2ème paramètre indique que variable sera incrémentée de la valeur de ce paramètre. Ainsi inc(n,3) incrémente-t-il n de 3. De même il existe une fonction dec() effectuant la décrémentation d'une variable (avec la présence optionnelle du pas de décrémentation).

    La boucle while permet de modifier de n'importe quelle façon les variables entrant dans les conditions de sortie. Ainsi on aurait pu écrire le programme précédent d'une autre façon :

    var 
        i,j : word;
    begin
        i:=7;
        j:=13;
        while i <> j do
        begin
            inc(i,7);
            inc(j,13);
        end;
        writeln('Le premier nombre divisible par 7 ',
                'et par 13 est : 'i);
        readln
    end.
    
    La boucle while contient plusieurs instructions : on insère donc un bloc d'nstructions avec begin et end.

    Lorsqu'on aura ajouté 12 fois 7 à i (i vaudra 7 × 13) et 6 fois 13 à j (j vaudra 13 × 7), on sortira de la boucle.

    La différence d'avec le programme précédent est :

    • il n'y a qu'une seule condition de sortie
    • on incrémente i de 7 et j de 13 à chaque boucle.
    L'opération d'addition met moins de temps microprocesseur qu'une division (même entière). On voit que, contrairement à la boucle FOR, vous avez toute liberté pour les conditions de boucles. Vous pouvez même utiliser des réels, des chaines de caractères etc... en conditions de sortie.

    Retour sur la somme des N premiers entiers

    Nous allons reprendre notre programme de somme des N premiers entiers. Avec la boucle while, vous pouvez interdire la saisie de certaines valeurs avant d'exécuter la boucle FOR :
    Program boucle2a;
    Var
        i,n ,r:LongInt;
    Begin
        r:=0;
        write ('Valeur de N ? (<65535) ');
        readln(n);
        while n > 65534 do
        begin
            write ('Valeur de N ? (<65535) ');
            readln(n);
        end;
        writeln ('le calcul par méthode itérative se fait...');
        for i:=1 to N do
            r:= r + i;
        writeln('La somme des ',n
                ,' premiers entiers (méthode itérative) est : '#10#13,
                r);
        readln;
    End.
    
    Avec la boucle while, vous avez un moyen d'éviter des erreurs système (dépassement de capacité par exemple, ou division par 0) ou dans les résultats système. Ce dernier programme montre qu'on est obligé d'exécuter une fois la demande de n avant la boucle while, et si, dès la première valeur entrée, n est inférieur à 65535, alors la boucle while ne s'exécute pas. Si on reprend le programme Boucle1c :
    Program boucle2c;
    Var
        i,n : LongInt;
        r:Real;
    Begin
        r:=0;
        write ('Valeur de N ? (< 2 milliards) ');
        readln(n);
        while n > 2000000000
            begin
            write ('Valeur de N ? (< 2 milliards) ');
            readln(n);
        end;
        writeln ('le calcul par méthode itérative se fait...');
        for i:=1 to N do
            r:= r + i;
        writeln('La somme des ',n
                ,' premiers entiers (méthode itérative) est : '#10#13,
                r:10:0);
        writeln ('Appuyez sur Entrée pour continuer');
        readln;
        writeln ('le calcul par méthode rapide se fait...');
        r:= n/2 * (n+1);
        writeln('La somme des ',n
                ,' premiers entiers (méthode rapide) est : '#10#13,
                r:10:0);
        readln;
    End.
    
    Ce programme reprend le même principe que boucle2a mais avec des valeurs de n beaucoup plus étendues!

    Dans vos logiciels utilisant la méthode rapide, vous devez utiliser une condition de validation (If ou while) au delà de laquelle le résultat serait faux. Pour cela, créer un algorithme comparant les résultats des 2 algorithmes (dans une boucle for) serait approprié.

    Exercices

    • Reprendre l'algorithme de la somme des carrés vu dans le paragraphe FOR. Etudier les valeurs limites possible pour N par rapport à R et l'intégrer dans une condition while comme le programme boucle2c ci-dessus,mais en n'utilisant que la méthode RAPIDE (r = n*(n+1)*(2*n+1)/6 ).
    • Ecrire un programme utilisant une variable réelle R que vous initialisez avec une valeur quelconque et un compteur i. Faites une boucle while de laquelle vous ne sortirez que quand R sera nul, sachant qu'à chaque boucle, vous divisez R par 2. Pourquoi R finit-il par prendre la valeur 0 alors qu'il s'agit d'un réel et, qu'à priori, R ne peut pas prendre la valeur 0 quand on le divise infiniment par 2 ?

    Important

    La condition de sortie d'une boucle while est NON (ensemble de condition pour continuer la boucle). Ainsi si vous avez un ensemble de conditions While condition 1 and condition2 ou condition3 la sortie s'effectuera avec (Non condition1) ou (Non condition2) et (non condition3).

  4. Boucle Repeat

    Au contraire de l'instruction while, l'instruction Repeat (Répéter) exécute une suite d'instructions avant de tester des conditions de sortie.

    On peut ainsi reprendre le programme boucle2a précédent en remplaçant l'instruction While par Repeat :

    Program boucle2a;
    Var
        i,n ,r:LongInt;
    Begin
        r:=0;
        Repeat
            write ('Valeur de N ? (<65535) ');
            readln(n);
        until n <= 65534;
        writeln ('le calcul par méthode itérative se fait...');
        for i:=1 to N do
            r:= r + i;
        writeln('La somme des ',n
                ,' premiers entiers (méthode itérative) est : '#10#13,
                r);
        readln;
    End.
    
    L'instruction Repeat évite de répéter un même jeu d'instruction si une condition incluse dans la boucle doit être testé à postériori.

    Les contitions de sortie sont indiquées après le mot clé Until (Jusqu'à), et ces conditions sont l'opposé des conditions pour rester dans la boucle (au contraire de While).

    Si un ensemble d'instructions doit être exécuté dans Repeat, on n'utilise PAS les mot clé Begin et End, puisque tout ce qui est entre Repeat et Until est exécuté!


  5. Instruction GOTO

    A l'instar d'autres langages de programmation plus "BASIC" (ainsi que le langage C), vous pouvez utiliser l'instruction
    GOTO. Cependant tous les bons enseignants en programmation informatique vous déconseilleront voire interdirons l'utilisation de cette méthode qui peut vite devenir un sac de noeuds dans la gestion des variables. Le LANGAGE Basic, qui ne possède pas de structures (pas de procédure ou fonction), ni de boucle WHILE est un langage impropre à la création de programmes solides et structurés (Hormis le GFA BAsic des Atari ST).

    Donc je me joins à ces enseignants (une pensée pour mon mentor Michel Lalos qui nous a quitté) pour INTERDIRE l'utilisation de GOTO dans les langages procéduraux tels que Pascal, C, Modula 2, Oberon, Java, PHP etc... Pour les BASIC interprétés, hélas, il n'y a pas d'autres possibilités.. (hormis pour le GFA Basic).


  6. Notions vues dans ce cours

    • Boucle For : incrémentation avec TO et décrementation avec DOWNTO
    • Algorithme de la somme des N premiers entiers
    • Optimisation de cet algorithme
    • Notion de complexité algorithmique
    • Tests sur les valeurs limites de variables : comment éviter les erreurs de dépassement de limites ?
    • Boucle While
    • Fonction inc() et dec()

Sommaire Tutoriel


Copyright © 2021 par Albatros Concept (Bruno Aubin)