• Aucun résultat trouvé

Comment dire ? (l'analyse de 1er niveau de la 1ère stratégie)

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) niveaux

1.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;