2 Diviser pour régner
1. Simulation du tirage du Lotto
1.5 Comment dire ? (l'analyse de 1er niveau de la 1ère stratégie)
Bien que nous n'ayons pas terminé le processus, nous sommes d'ores et déjà en mesure d'exprimer en Pascal le programme principal qui rendra compte de l'analyse de premier niveau
menée. Nous ne pouvons évidemment pas encore compléter les textes des trois procédures qu'il reste à préciser, mais nous en rappellerons, sous forme de commentaires, les spécifications, c'est-à-dire essentiellement la teneur des 3 rectangles ci-dessus.
program LOTTO;
(* Il fait simuler un tirage du lotto. On y utilise un tableau à 7 composantes pour y stocker les résultats obtenus *)
uses WinCRT9;
(* pour pouvoir utiliser les procédures de gestion d'écran comme Clrscr ou Gotoxy en Turbo
Pascal sous Windows *) (1)
const NNP = 42; (* le plus grand numéro pouvant être obtenu *)
NNT = 7; (* nombre de numéros tirés (y compris le complémentaire) *) (2)
type NumerosPossibles = 1..NNP; (3)
var Compteur: integer;
(* compteur pour les boucles for *)
Tirages : array[1..NNT] of NumerosPossibles; (* qui contiendra les numéros obtenus *)
Reponse : char;
(* réponse de l'utilisateur *)
procedure AVERTIR; (4)
(* Fait afficher l'écran - titre (cf. les spécifications générales) et attend la frappe d'Entrée avant d'effacer)*)
procedure TIRER;
(* elle fait placer dans le tiroir n° Compteur de l'étagère Tirages un nombre au hasard entre 1 et NNP et qui soit à coup sûr différent des contenus des tiroirs précédents.*)
procedure AFFICHER;
(* elle fait afficher les contenus des tiroirs de Tirages sous la forme d'un écran qui respecte les spécifications générales*)
begin
randomize; (5)
AVERTIR; repeat
ClrScr; (* pour effacer avant de recommencer un tirage *)
Tirages[1]:=random(NNP)+1; for Compteur := 2 to NNT do TIRER;
AFFICHER;
gotoxy(4,20); (6)
write(' Recommence-t-on un tirage (O ou N)?'); readln(Reponse);
Reponse:= upcase(Reponse); (7)
until Reponse = 'N'; end.
1.5.1 Commentaires sur le programme Pascal
(1) C'est ici un détail, propre à la version envisagée de Turbo-Pascal. Ces détails sans grande importance changent avec l'implémentation choisie.
(2) C'est le mot réservé const qui annonce en Pascal la définition des constantes. Elle doit précéder les déclarations de type et de variable et associe donc un identificateur (qui respecte la syntaxe Pascal habituelle) à une valeur constante.
On peut définir des constantes entières, réelles, booléennes, chaînes de caractères, ...
Attention, c'est le symbole = qui lie l'identificateur à la constante identifiée.
(3) On définit ici, puisque Pascal le permet, un type intervalle (parmi les entiers). Les entiers entre 1 et NNP seront dorénavant rebaptisés comme étant du type NumerosPossibles.
(4) On sait que c'est entre la partie déclaration et la partie exécutable que les textes des procédures (correspondant aux actions complexes) viendront se nicher.
L'ordre dans lequel les diverses procédures s'y présentent n'a rien à voir avec l'ordre dans lequel s'effectuent les appels de ces procédures au sein de la partie exécutable.
(5) L'appel de la procédure randomize est obligatoire si l'on veut que la fonction random fournisse des séries de nombres aléatoires différentes à chaque exécution. En son absence, à chaque nouvelle exécution du programme, c'est la même suite de tirages aléatoires qui est produite.
(6) J'ai d'emblée tenu compte de la présentation demandée avec l'envoi du curseur en bas de l'écran pour l'affichage de la question à l'utilisateur. C'est la procédure gotoxy( , ) qui fait effectuer ce saut du curseur. (Elle fait partie dans l'implémentation Turbo Pascal 1.5 sous Windows, de la librairie WinCRT.)
(7) Un tout petit détail qui me permet de présenter un outil (de genre fonction) : upcase qui transforme les minuscules en majuscules (sauf malheureusement les minuscules accentuées qui restent ce qu'elles sont - nous y reviendrons -).
De cette manière, que la réponse de l'utilisateur soit "n" ou "N", upcase la transforme en "N".
?
Aurait-on pu se passer de upcase en arrêtant cependant dès que la réponse de l'utilisateur est "n" ou "N" ? 1.6 Analyse de second (puis troisième) niveaux1.6.1 Analyse de TIRER
Nous savons parfaitement "Quoi faire ?" (C'est ce qui est explicité dans le rectangle de la page 70). Il reste à trouver d'abord "Comment faire ?".
1.6.1.1 TIRER ? Comment faire ?
Imaginons un instant la situation :
Tirages 12 1 23 2 6 3 4 5 6 NNT
La tâche étant ici parfaitement simple (il nous faut placer un nombre convenable dans le tiroir n° 4), la découverte d'une stratégie est immédiate.
- On tire un nombre au hasard, on vérifie qu'il est bien distinct du contenu des tiroirs (de 1 à pred(Compteur)) et on recommence éventuellement jusqu'à ce qu'il n'y ait pas égalité (du tirage effectué avec les contenus des tiroirs précédents).
Compteur
1.6.1.2 TIRER ? Comment faire faire ?
Un premier jet conduit à Répéter
Place un nombre au hasard entre 1 et NNP dans le tiroir n° Compteur de Tirages
FAIS TOUT CE QU'IL FAUT POUR VERIFIER S'IL Y A EGALITE ENTRE LE NOMBRE AINSI PLACE DANS LE TIROIR Compteur ET L'UN DES TIROIRS PRECEDENTS.
jusqu'à ce que il n'y ait pas égalité
ou sous forme condensée Répéter
Tirages[Compteur] ← un nombre au hasard entre 1 et NNP VERIFIER
jusqu'à ce que il n'y ait pas égalité
Je ne puis évidemment pas me contenter de la condition "il n'y ait pas égalité" énoncée sous cette forme.
Il faut qu'à l'issue de l'action VERIFIER, "quelque chose" ait (éventuellement) changé et signale qu'il y a ou non égalité (entre le nombre qui vient d'être tiré et l'un des précédents). Ce "quelque chose", ce ne peut être que le contenu d'une variable, dont l'action VERIFIER aura pour but, justement, de déterminer le contenu.
Cette variable, je la choisis de type booléen et je la baptise Egalite, ce qui me permet de préciser la marche à suivre.
Répeter
Tirages[Compteur] ← un nombre au hasard entre 1 et NNP VERIFIER
jusqu'à ce que pas Egalite Egalité de type booléen
Il me reste bien entendu, puisqu'une action complexe VERIFIER a été mise en évidence, à en préciser exactement la teneur :
VERIFIER
Compteur →
de type entier
Tirages →
cf. plus haut
Placer vrai dans la variable Egalite si le contenu du tiroir n° Compteur de Tirages est identique à l'un
des contenus des tiroirs précédents et faux sinon. → Egalite de type booléen avec
Avant de VERIFIER Après VERIFIER - Compteur est compris entre 2 et NNT
- Les tiroirs de 1 à pred(Compteur) de
Tirages contiennent des entiers distincts
entre 1 et NNP
- Compteur n'a pas changé - Tirages n'a pas changé
- Egalite contient vrai si le tiroir n° Compteur était identique à l'un des précédents et faux sinon.
Il faut reconnaître que ce formalisme est, en ce qui concerne l'exemple traité ici, assez lourd : la description du "Quoi faire ?" est à chaque fois plus longue que la marche à suivre à laquelle elle donnera naissance!
Cependant, il vaut mieux, me semble-t-il présenter et faire apprécier (et si possible admettre) la méthodologie sur un exemple "simple" (même si sa justification n'apparaît dans ce cas pas très claire) plutôt que de le faire dans un cas où la complexité du problème traité réclame trop d'attention au détriment de la compréhension initiale de cette méthodologie.
Nous sommes dès à présent en mesure d'écrire :
1.6.1.3 TIRER ? Comment dire ?
procedure TIRER;
(* elle fait placer dans le tiroir n° Compteur de l'étagère Tirages un nombre au hasard entre 1 et NNP et qui soit à coup sûr différent des contenus des tiroirs précédents.*)
var Egalite : boolean;
(* vraie lorsque le numéro tiré est identique à l'un des précédents *)
procedure VERIFIER;
(* elle fait placer vrai dans la variable Egalite si le contenu du tiroir n° Compteur de
Tirages est identique à l'un des contenus des tiroirs précédents et faux sinon.*)
begin repeat
Tirages[Compteur]:= random(NNP)+1; VERIFIER;
until not Egalite; end;
1.6.2 Analyse de VERIFIER
Nous avons à nouveau isolé une tâche (et un problème de programmation) en soi. Nous pouvons oublier tout le reste pour nous concentrer sur ce seul et petit problème. Retour à la case départ, donc.
1.6.2.1 VERIFIER ? Comment faire ?
Comparaisons successives ⇓⇓⇓⇓ ⇓⇓⇓⇓ ⇓⇓⇓⇓ Tirages Egalite 12 1 23 2 6 3 23 4 5 6 NNT
La stratégie est immédiate
- On va successivement comparer le tiroir n° Compteur aux précédents
- Ces comparaisons s'arrêtent dès qu'une égalité est signalée (Egalite devenant alors vraie) ou au plus tard avec le tiroir précédent celui portant le n° Compteur.
- Lorsqu'aucune égalité n'est détectée, Egalite prend la valeur faux.
1.6.2.2 VERIFIER ? Comment faire faire ?
?
Quelle marche à suivre proposeriez-vous pour rendre compte de cette stratégie ?L'essentiel est de traduire la boucle des comparaisons successives qui s'arrêteront à la détection d'une égalité ou lorsque tous les tiroirs précédents celui qui est testé ont été passés en revue.
On peut proposer :
Compteur
Place 0 dans C C de type entier
Répéter
Place succ(C) dans C
Egalite ← Tirages [C] = Tirages [Compteur]
jusqu'à ce que C=pred(Compteur) ou Egalite
1. On constate qu'une nouvelle variable de type "compteur" est indispensable pour écrire la boucle d'exploration. Nous sommes libres du choix de son nom : le seul qui nous soit interdit est Compteur, puisque cette variable préexiste et doit comme telle pouvoir être consultée (Cf. le rectangle spécifiant VERIFIER ci-dessus). Nous avons donc baptisé C ce compteur. 2. La manière dont la comparaison est écrite peut sembler surprenante :
Egalite ← Tirages [c] = Tirages [Compteur]
Il n'y a pourtant rien que de très normal : on commande de remplir une variable booléenne (qui ne peut donc accueillir que les valeurs vrai ou faux) et l'information qu'on demande d'y placer (Tirage [C] = Tirages [Compteur]) est bien une expression booléenne qui aura forcément les valeurs vrai ou faux. (A rapprocher de la page 39).
?
?
?
L'affectation
Egalite ← Tirages [C] = Tirages [Compteur] est elle équivalente à
Si Tirages[C] = Tirages[Compteur] alors
Egalite ← vrai
Disposant de la description de la manière dont l'action VERIFIER sera menée à bien, nous somme en mesure d'écrire
1.6.2.3 VERIFIER ? Comment dire ?
procedure VERIFIER;
(* elle fait placer vrai dans la variable Egalite si le contenu du tiroir n° Compteur de
Tirages est identique à l'un des contenus des tiroirs précédents et faux sinon.*)
var C : integer; (* compteur de boucle *) begin C:=0; Repeat C:=succ(C); Egalite:=Tirages[C]=Tirages[Compteur]; until (C=pred(Compteur)) or Egalite; end;
1.6.3 Analyse de AFFICHER
Comme toujours, le "Quoi faire ?" a été précisé dans le rectangle de spécification (Cf. page 71).
1.6.3.1 AFFICHER ? Comment faire ?
Il suffira d'annoncer les 6 (en vérité, plutôt pred(NNT)) premiers numéros contenus dans
1.6.3.2 AFFICHER ? Comment faire faire ?
Ici aussi une variable du genre compteur sera indispensable. Mais en relisant les spécifications de AFFICHER et particulièrement la liste des variables consultées (page 71), on s'aperçoit que seule Tirages est citée.
On a dès lors parfaitement le droit d'appeler Compteur la nouvelle variable à définir pour écrire la boucle permettant l'affichage. On verra que cette variable Compteur "locale" n'a rien à voir avec la variable Compteur définie lors de l'analyse de 1er niveau, dont on ignore en principe totalement l'existence ici.
Rappelons une fois de plus que les seules contraintes imposées à ceux qui auraient à analyser un module (comme AFFICHER) sont celles reprises dans le rectangle descriptif qui précise exactement quelles sont les variables imposées. Pour les variables définies localement lors de l'analyse, on a donc toute liberté de choix, mis à part l'obligation d'éviter de les nommer comme ces variables imposées.
On écrira donc : Efface l'écran
Affiche 'Voici les résultats :' (en ligne 2, colonne 4)
Pour Compteur allant de 1 à pred(NNT) Compteur de type entier
Affiche Tirages [Compteur] (en ligne 6, sans passage à la ligne)
Affiche 'et le numéro complémentaire', Tirages [NTT] (en ligne 10, colonne 4) qui se traduit en :
1.6.3.3 AFFICHER ? Comment dire ?
procedure AFFICHER;
(* elle fait afficher les contenus des tiroirs de Tirages sous la forme d'un écran qui respecte les spécifications générales*)
var Compteur : integer; begin
gotoxy(4,2);
write('Voici les résultats du Lotto :'); gotoxy(8,6);
for Compteur:=1 to pred(NNT) do write(Tirages[Compteur]:4); gotoxy(4,10);
write('et le numéro complémentaire : ', Tirages[NNT]); end;
Nous avons gardé pour la fin le plus facile :
1.6.4 Analyse de AVERTIR
On peut très certainement se contenter de
1.6.4.1 AVERTIR ? Comment dire ?
procedure AVERTIR;
(* Fait afficher l'écran - titre (cf. les spécifications générales) et attend la frappe d'Entrée avant d'effacer)*)
begin clrscr;
writeln('Je vais procéder pour vous au tirage des numéros du Lotto'); write('Frappez Entrée');
readln; (* lecture de la frappe de la touche Entrée *)
clrscr; end;