Structures conditionnelles en Pascal

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


A partir de maintenant nous allons commencer à écrire des programmes qui réagissent en fonction de certains paramètres. Pour ça nous allons utiliser les structures conditionnelles.


Sommaire

if -then - else

Cette structure correspond à ce qu'on appelle une alternative simple.
Entre le if et le then doit figurer une valeur booléenne entre parenthèses (autant prendre les bonnes habitudes dès le début). Cette valeur peut être modifiée par les opérateurs que vous avez vu dans le cours sur les types définis.

Après le then se situe le traitement à effectuer si la condition est vérifiée. On peut éventuellement voir après un else suivi d'une autre instruction qui est exécutée si la condition du if n'était pas vérifiée, mais cette clause n'est pas obligatoire.

Voici un exemple dans lequel on demande à l'utilisateur de fournir deux valeurs. On lui donne ensuite le minimum et le maximum...

program Condition_if;

(* uses WinCrt; *)

(* declaration de variables *)
var I, J     : integer;
    Min, Max : integer;

(**** Debut du programme ****)
begin
  (* entrees *)
  write('Entrez deux entiers : ');
  readln( I, J);

  (* traitement *)
  if ( I < J) then
    Min := I; (* fin de l'instruction (point-virgule) *)
    Max := J;
  (* pas de else *)

  if ( J <= I) then
    Min := J;
    Max := I;
  (* pas de else *)

  (* sorties *)
  writeln;
  writeln('Minimum : ', Min);
  writeln('Maximum : ', Max);

  readln; (* pause *)    
end.

Par chance notre programme fonctionne si l'on rentre les entiers dans un ordre décroissant... N'oubliez pas qu'en Pascal la fin d'une instruction est marquée par le point-virgule ; et qu'une seule instruction peut suivre le then ou le else d'une structure de choix !

Astuce
Pour exécuter plusieurs instructions en fonction d'une condition, la seule solution est de construire un bloc begin ... end afin que le programme considère ceci comme une instruction unique.
program Condition_if;

(* uses WinCrt; *)

(* declaration de variables *)
var I, J     : integer;
    Min, Max : integer;

(**** Debut du programme ****)
begin
  (* entrees *)
  write('Entrez deux entiers : ');
  readln( I, J);

  (* traitement *)
if ( I < J) then
  begin
    Min := I;
    Max := J;
  end;

if ( J <= I) then
  begin
    Min := J;
    Max := I;
  end;


  (* sorties *)
  writeln;
  writeln('Minimum : ', Min);
  writeln('Maximum : ', Max);

  readln; (* pause *)    
end.

Cette fois tout marche correctement. Mais nous n'allons pas laisser le programme sous cette forme. Vous avez certainement remarqué que les conditions I < J et J <= I ne peuvent pas être vraies en même temps.<brPour être plus précis il s'agit justement des propositions contraires ; c'est pourquoi nous alons utiliser une structure if then else complète ;o]

En Pascal une instruction if peut contenir n'importe quelle instruction valide, vous pourrez facilement imaginer qu'une condition puisse à son tour contenir une condition d'exécution. C'est à dire qu'il est possible d'imbriquer les conditions.
On se propose d'écrire un programme demandant à l'utilisateur d'entrer un chiffre au clavier et ensuite de l'afficher littéralement.

program Chiffre;

(* uses WinCrt; *)

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

begin
  (* entrees utilisateur *)
  write ( 'Donnez un chiffre : ');
  readln ( Nombre);

  (* sorties *)
  if      ( Nombre=0) then
    writeln ( 'zero')
  else if ( Nombre=1) then
    writeln ( 'un')
  else if ( Nombre=2) then
    writeln ( 'deux')
  else if ( Nombre=3) then
    writeln ( 'trois')
  else if ( Nombre=4) then
    writeln ( 'quatre')
  else if ( Nombre=5) then
    writeln ( 'cinq')
  else if ( Nombre=6) then
    writeln ( 'six')
  else if ( Nombre=7) then
    writeln ( 'sept')
  else if ( Nombre=8) then
    writeln ( 'huit')
  else if ( Nombre=9) then
    writeln ('neuf')
  else (* ce n'est pas un chiffre *)
    writeln ( 'un chiffre est demandé');

  readln; (* pause *)
end.

Remarque
Encore une fois il est possible d'utiliser les blocs d'isntructions. Ca deviendra même rapidement indispensable :)


case - else - end

Nous venons de voir qu'avec l'alternative simple il est possible de traiter toutes les conditions imaginables (n'oubliez pas u'il est possible d'employer des opérateurs booléens), alors à quoi peut bien servir une autre structure alternative ? La sélection par cas (case) permet de rendre vos programmes bien plus clairs en remplaçant les if imbriqués dans certains cas.
Regardez donc ce que deviens notre programme précédent écrit avec cette nouvelle technique, ça vaut certainement mieux qu'un long discours :

program SelectionParCas;

(* uses WinCrt; *)

(* declaration de varible *)
var Nombre : integer;

begin
  (* entrees utilisateur *)
  write ( 'Donnez un chiffre : ');
  readln ( Nombre);

  (* sorties *)
  case Nombre of
    0 : writeln('zero');
    1 : writeln('un');
    2 : writeln('deux');
    3 : writeln('trois');
    4 : writeln('quatre');
    5 : writeln('cinq');
    6 : writeln('six');
    7 : writeln('sept');
    8 : writeln('huit');
    9 : writeln('neuf')
    else
      writeln('Un chiffre est demande !')
  end; (* fin de la selection par cas *)

end.

Evidemment, comme pour l'alternative simple, il est possible d'utiliser des conditions bien plus complexes graces aux opérateurs booléens ou encore d'instaurer des traitements plus complexes en construisant des blocs.
Le else n'est pas indispensable, mais à moins que vous ne citiez toutes les valeurs que le type scalaire en question peut prendre laissez le !

Traiter une condition à l'aide de l'instruction if est envisageable quelque soit le type de la donnée, en revanche ce n'est pas possible avec un case car le type utilisé doit nécessairement être scalaire, c'est à dire avoir un nombre de valeurs finies (comme integer, char, boolean ou les autres types construits sur les entiers). A défaut vous serrez obligés de revenir sur une instruction if imbriquée.

Vous pourrez également voir apparraître des intervalles de valeurs ou des valeurs multiples pour lesquelles on effectuera le même traitement. Les intervalles verront apparaître leur valeur minimale et leur valeur maximale séparées par un ..
Quant aux valeurs multiples, elles seronts séparées par une virgule , mais regardez plutôt l'exmple suivant dans lequel on demande à l'utilisateur d'entrer un caractère, on lui dit ensuite s'il s'agit d'une lettre majuscule, d'une lettre inuscule, d'un chiffre etc.

program Caractere;

(* uses WinCrt; *)

(* déclaration de variable *)
var Car : char;

begin
  (* entrées utilisateur *)
  write ( 'Entrez un caractère : ');
  readln ( Car);

  (* sorties *)
  case Car of
    'a' .. 'z'                   : writeln ( 'Lettre minuscule');
    'A' .. 'Z'                   : writeln ( 'Lettre majuscule');
    '0' .. '9'                   : writeln ( 'Chiffre');
    '.', ',', ';', ':', '!', '?' : writeln ( 'Ponctuation');
    'é', 'à', 'è', 'ù'           : writeln ( 'Lettre accentuée')
    else
      writeln ( 'Autre type de caractère')
  end
end.

Danger
Attention si vous êtes habitué d'autres langages tels que le C/C++ ou le Java. En Pascal une fois qu'un cas de la sélection est compatible avec la valeur entrée, le programme s'arrête là et n'essai pas de comparer la valeur traitée aux autres filtres.
En d'autres termes, dès qu'un cas est compatible avec la valeur de départ, le compilateur exécute l'instruction associée et saute directement au end de la structure.


Execrcice : résolution d'un trinôme

A ce stade du cours je vous propose un petit exercice afin d'utiliser vos nouvelles connaissances dans un exemple concret. Nous allons résoudre un trinôme (équation du second degré).
Je pars du principe que vous connaissez tous la méthode à employer, il s'agit de calculer le discriminant et en fonction de sa valeur d'informer l'utilisateur des différentes solutions.

Vous allez donc commencer par demander les coefficients de cette équation à l'utilisateur. Ensuite vous calculerez la valeur de ce discriminant et vous agirez en conséquences (solutions réelles, solution réelle double ou solution complexe).
Attention, veillez à ce que le coefficient du monôme de plus haut degré (x2) ne soit pas nul, sans quoi il faudrait envisager d'autres cas ; si celà se produit on mettra fin au programme avec l'instruction halt.

program Trinome;
(* utilisant de if *)

(* 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');
  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 !');
      halt(1); (* renvoi le code d'erreur 1 au système d'exploitation*)
    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.

Question
Libre à vous d'afficher les différentes solutions si vous voulez vous entrainer. Il est même envisageable de les afficher sous forme exacte, c'est à dire en vous débrouillant pour écrire une fraction complète de vous même (donc sans faire les calculs).
Si vous en voulez encore plus débrouillez vous à) présenter le résultat de manière naturelle, c'est à dire avec l'éventuel trait de fraction au milieu et le numérateur et dénominateur de part et d'autre. Si vous voulez de l'aide posez vos questions sur le forum.

Ca marche très bien mais le traitement que nous avons pour A = 0 n'est pas satisfaisant. Dans un programme réel, l'utilisateur ne serait pas très content de voir le programme se terminer pour ce motif.
Deux cas sont envisageables, soit on traite le cas où A = 0 qui se ramène à une équation du premier degré, soit on demande à l'utilisateur de ressaisir une valeur pour A.


Les labels

Les labels (ou étiquettes en français) sont des instructions particulières qui permettent de renvoyer à un endroit précis dans le programme. Un label en soit est complètement innutile, il doit toujours être associé à une (ou plusieurs) instruction goto ailleurs dans le code source.
Afin qu'il n'y ait pas d'erreurs, les labels, comme les variables, doivent être déclarés en début de programme comme nous le verrons ci-dessous.

Ensuite un label intervient au sein d'un programme immédiatement suivi du signe : (sans espaces). On fait un branchement, c'ets à dire un renvoit à ce label en utilisant une instruction goto ailleurs dans le programme.
Nous pourrions donc utiliser cette technique pour redemander à l'utilisateur de saisir la valeur de A dans notre programme précédent si celle-ci n'est pas correcte.

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.

Remarque
Pour l'instant vous ne disposez d'aucune autre méthode, mais vous verrez dès le cours suivant qu'il est possible de remplacer les labels par autre chose dans n'importe quel programme.
Avec les labels la lisibilité du code source est réduite et la maintenance n'en est que plus difficile.
Outils personels