Structures itératives en Pascal

Un article de Mangue.org, l'encyclopéde libre.


Les structures itératives vont vous permettre de répéter plusieurs fois un traitement de manière élégante.


Sommaire

repeat - until

Reprenons le programme que nous avons écrit à la fin du [./cours7.htm cours précédent], celui-ci permettait de résoudre un trinôme :

program Trinome;

(* uses WinCrt; *)

label Entrees;

var A, B, C : real; (* coefficients *)
    Delta   : real; (* discriminant *)

(**** Debut du programme ****)
begin
Entrees:  (* entrees utilisateur *)
  writeln ('Resolution d''un trinome Ax²+Bx+C=0');
  write ('Entrez les coefficients A, B et C : ');
  readln ( A, B, C);

  (* verification des entrees *)
  if ( A = 0.0) then
    begin
      writeln ( 'A doit etre non nul !');
      goto Entrees
    end;

  (* traitement des donnes *)
  Delta := B*B - 4*A*C;

  (* sorties *)
  if ( Delta < 0.0) then
    writeln('Le trinome a deux solutions complexes')
  else if ( Delta = 0.0) then
    writeln('Le trinome a une solution reelle')
  else
    writeln('Le trinome a deux solutions reelles');

  readln (* pause *)
end.

Dans ce programme nous répétions la demande des coefficiants de notre trinôme jusqu'à ce que la valeur de A soit non nulle.
Nous avons implémenté celà grace à un label et une condition puisqu'il s'agit de la seule méthode que nous connaissions alors. Mais ce n'est pas la meilleure solution, il est préférable d'utiliser une structure itérative qui assure elle même la répétition du traitement en fonction d'une condition.

Dans notre exemple il s'agit d'effectuer le traitement jusqu'à ce que A soit non nul. Il existe justement une structure itérative repeat - until permettant de faire un traitement et éventuellement de le répéter si la condition de l'itération n'est pas vérifiée.
Mais plutôt que de vous noyer sous des explications, analysez plutôt cet exemple qui remplacera notre programme :

program Trinome;

(* uses WinCrt; *)

var A, B, C : real; (* coefficients *)
    Delta   : real; (* discriminant *)

(**** Debut du programme ****)
begin
  (* entrees utilisateur *)
  writeln ('Resolution d''un trinome Ax²+Bx+C=0');

  repeat
    write ('Entrez les coefficients A, B et C avec A non nul ! ');
    readln ( A, B, C)
  (* verification des entrees *)
  until ( A <> 0.0)

  (* traitement des donnes *)
  Delta := B*B - 4*A*C;

  (* sorties *)
  if ( Delta < 0.0) then
    writeln('Le trinome a deux solutions complexes')
  else if ( Delta = 0.0) then
    writeln('Le trinome a une solution reelle')
  else
    writeln('Le trinome a deux solutions reelles');

  readln (* pause *)
end.

La syntaxe est assez intuitive, le repeat marque le début du bloc d'itération qui est alors terminé par l'apparition du mot clé until et de sa condition biensûr.
Encore une fois même si ce n'est pas nécessaire je place la condition entre parenthèses ;-)

Remarque
Le smots clé repeat et until délimitent à eux seuls un bloc. C'est pourquoi vous ne voyez pas apparaître de begin - end à l'intérieur de ceux-cis.

Dans un bloc repeat - until, l'évaluation de la condition d'itération se fait à la fin de la boucle, c'est pourquoi le tout sera exécuté au moins une fois quoi qu'il arrive !
Essayez donc de compiler et d'exécuter ce programme si vous n'en êtes pas convaincu, la condition est fausse bien évidemment mais le traitement a bien été effectué !

program DemonstrationDeLInterpretationDuRepeatUntil;

(* uses WinCrt; *)

begin
  repeat
    write('Cette condition est fausse')
  until (3 < 1)
end.


while - do

Comment procéder alors si on ne souhaite pas que l'itération soit traitée à tous les coups. Dans la plus part des programmes, le traitement devra ne pas être exécuté du tout si la condition d'itération est fausse !
Heureusement pour nous en Pascal il existe une structure while - do effectuant le même type d'itérations qu'un repeat - until à ceci près que la condition d'itération est justement évaluée en début de boucle.

Voici déjà une exemple d'utilisation de cette structure. Dans le programme qui suit nous demandons à l'utilsateur de fournir un nombre entier positif, ensuite nous lançons un compte à rebours de ce nombre jusqu'à 0 forcément ;-)

program CompteARebours;

(* uses WinCrt; *)

(* constantes du programme *)
const CrLf = chr(10) + chr(13); (* retour chariot *)

(* declaration des variables *)
var Nombre : integer;

(* debut du programme *)
begin
  (* entrees utilisateur *)
  write( CrLf, 'Entrez un nombre entier positif : ');
  readln( Nombre);


  (* 2 lignes vides *)
  writeln( CrLf);

  (* sorties ecran *)
  while ( Nombre >= 0) do
   begin
     writeln ( Nombre);
     dec ( Nombre) (* Nombre := Nombre - 1 *)
   end;

  (* pause *)
  readln
end.

Le programme nous affiche un joli petit compte à rebours si la condition initiale est vrai ; en revanche il n'affiche rien si la condition était fausse à son arrivée sur le while (Nombre <= 0) c'est à dire si l'utilisateur a fournit un nombre négatif en entrée.

Danger
Ne faites pas l'erreur de confondre une itération de type while à une itération de type repeat.
La première ne boucle que sur une seule instruction alors que la seconde applique l'itération sur un bloc d'instructions. Ainsi si vous voulez construire une itération avec plusieurs instructions pour un while vous devrez penser à utiliser un bloc begin - end

Un petit exercice vous permettra d'ores et déjà de manipuler les itérations. Je vous propose d'écrire un programme qui affiche l'alphabet en utilisant une itération (plusieurs solutions sont possibles).
Pensez à utiliser les fonctions inc et dec qui permettent d'incrémenter et décrémenter des entiers biensûr, mais aussi des caractères !

program Alphabet;
(* une solution possible *)

(* uses WinCrt; *)

(* declaration des variables *)
var Lettre : char;

(* debut du programme *)
begin
  (* initialisation *)
  Lettre := 'A';

  (* saute une ligne *)
  writeln;

  (* affichage de l'alphabet *)
  while ( Lettre <= 'Z') do
   begin
     write ( Lettre);
     inc ( Lettre)
   end;

  (* pause *)
  readln
end.

Encore un exercice... Cette fois je vous demanderai simplement de calculer la factorielle d'un nombre entier entré par l'utilisateur.

Remarque
Rappel mathématique : La factorielle d'un nombre entier N positif est la multiplication entre eux de tous les nombres compris entre 1 et N.
On prend comme convention que la factorielle de 0 notée 0! et lu factorielle 0 est égale à 1
On peut également démontrer que N! = N * (N-1)!
program Factorielle;

(* uses WinCrt; *)

(* declaration des variables *)
var Nombre : integer; (* argument *)
    Temp   : integer; (* utile pour le calcul *)
    Fact   : integer; (* factorielle *)

begin
  writeln;

  (* entrees *)
  repeat
    write ( 'Argument : ');
    readln ( Nombre)
  until ( Nombre >= 0)

  (* initialisation *)
  Fact := 1;
  Temp := Nombre;

  (* calcul *)
  while ( Temp > 1) do
   begin
     Fact := Fact * Temp;
     dec ( Temp)
   end;

  (* sorties *)
  writeln;
  write(Nombre,' a pour factorielle : ', Fact);
  readln (* pause *)
end.

Vous devriez être capable d'expliquer vous même ce qu'il arrive pour le calcul d'une factorielle supérieure à 7. Un overflow apparaît, pour l'éviter on pourrait tout simplement interdire le calcul de ces factorielles ou alors changer de type d'entier afin de permettre le codage dans l'ordinateur de nombres plus grands.


for - to/downto - do

Dans la plus part des exemples que nous avons u jusqu'à présent nous faisions intervenir des incrémentations ou des décrémentations à chaque itération. Ce genre de traitement est fréquent, de plus lorsque la condition d'itération dépend elle même ce cette incrémentation (ou décrémentation) on peut remplacer la struture while concernée par un boucle de type for

Vous convinedrez que si une boucle est écrite avec ce procédé la lisibilité en sera accrue ! Vous en doutez encore ? Regardez ce que devient le programme CompteARebours :

program CompteARebours;

(* uses WinCrt; *)

(* constantes du programme *)
const CrLf = chr(10) + chr(13); (* retour chariot *)

(* declaration des variables *)
var Nombre : integer;
    Temp   : integer;

(* debut du programme *)
begin
  (* entrees utilisateur *)
  write( CrLf, 'Entrez un nombre entier positif : ');
  readln( Nombre);

  (* 2 lignes vides *)
  writeln( CrLf);

  (* sorties ecran *)
  for Temp := Nombre downto 0 do (* ligne 22 *)
    writeln ( Temp);

  (* pause *)
  readln
end.

Une boucle for fera nécessairement intervenir une variable de contrôle qui sera intialisée au sein même de la structure. Vous devrez enfin fournier une valeur limite qui permettra de déterminer quand mettre fin aux itérations et enfin un mot clé permettant de savoir si on incrément ou décrémente entre chaque itération (donc to ou downto repectivement)

Lorsque le compilateur arrive sur une instruction telle que celle-ci il commence par affecter la valeur initiale à notre variable de contrôle (donc ici Temp := Nombre), il compare ensuite cette valeur à la valeur finale en fonction du type de pas demandé (incrémentation ou décrémentation), si le traitement peut être effectué en fonction du résultat de cette comparaison pas de problèmes ! Sinon le traitement n'a plus lieu.
De plus si vous essayiez par exemple d'incrémenter votre variable de contrôle de 4 (valeur initiale) à 2 (valeur finale) et bien rien ne se passerait ! On sauterait simplement la structure itérative !
Dans tous les cas et quelque soit le traitement, il est possible de rammener une boucle for à une boucle while.

Danger
Il est important de veiller à ce que la valeur de votre variable de contrôle ne soit pas modifiée par les instructions de l'itération.
Si votre programme est bien construit, la valeur de cette variable ne doit pas être modifiée d'une autre manière qu'avec les incrémentations/décrémentations programmées par la boucle for elle même !
Remarque
Contrairement à d'autres langages il est impossible en Pascal de spécifier le pas d'une incrémentation/décrémentation. Impossible donc d'aller de la valeur initiale à la valeur finale en ajoutant 5 à chaque fois par exemple !

Je vous propose un exercice simple. L'utilisateur entre un nombre entier et vous devez lui afficher la table de multiplication de ce nombre ! Voici une correction possible :

program Tables;

(* uses WinCrt; *)

const Maximum = 10;

(* declaration des variables *)
var Nombre : byte; (* entier de 0 à 255 *)
    I      : byte;

(**** debut du programme ****)
begin
  writeln;

  (* entrees *)
  repeat
    write('Vous voulez la table de ? ');
    readln(Nombre)
  until ( (Nombre > 1) and (Nombre <= 255));

  (* affichage *)
  writeln; (* saute une ligne *)
  for I := 1 to Maximum do
   begin
    write(I:3, ' * ', Nombre:3);
    writeln(' = ', (I*Nombre):7)
   end;
  readln (* pause *)

end.


boucles infinies

Même si vous faites très attention, quand vous écrirez des programmes importants vous vous retrouverez certainement avec une "boucle infinie"...
Cela peut provenir de plusieurs facteurs, mais les conséquences sont les mêmes... Votre programme tournera en rond sans que vous ne puissiez le terminer normalement (comme vous l'aviez prévu).

Astuce
Si un de vos programmes est bloqué ou semble ne plus réagir pensez aux combinaisons de touches Ctrl + Break (arrêt défil parfois) ou Ctrl + Attn (ou encore Pause). Ctrl + C permet aussi d'arrêter le programme, mais seulement lorsqu'il est sur une procédure d'Entrée / Sortie.
Il est inutile de se jeter sur les trois touches préférées de Bill Gates, gardez-les en dernier recours ;o)

Pourtant, il peut arriver dans certaines situations que vous ayez besoin de créer une boucle infinie dans vos programmes. Ceci pour introduire un point d'échapement en plein milieu des instructions qui la composent par exemple.
Je ne conseille pas ce genre de procédé qui nuit gravement à la lisibilité du code source...
Pourtant dans certains cas il est impossible de s'en passer. Pensez donc aux procédures break et continue, consultez l'aide de votre compilateur ou posez vos questions sur notre forum si vous ne comprenez pas...

Outils personels