• Aucun résultat trouvé

3 Les limites de l'approche descendante

1. Une calculatrice pour le grand Jules

1.6 Analyse de DECIMALISER .1 DECIMALISER ? Quoi faire ?

1.8.3 ROMANISER_TOUT_OU_PARTIE. Comment faire faire ?

Il nous reste à formaliser la stratégie décrite, après avoir mis en évidence le tableau qui nous sera indispensable.

1.8.3.1 Structure de données : le tableau nécessaire

Il doit rendre compte des concordances présentées par le tableau décrit ci-dessus : unité → I et 5 unités → V

dizaine → X et 5 dizaines → L centaine → C et 5 centaines → D millier → M.

Valeurs

1 5 10 50 100 500 1000

I V X L C D M

Malheureusement, si ce tableau permettait d'établir fort rapidement une correspondance chiffre romain → valeur décimale, il ne permet pas aisément d'établir la correspondance inverse, et c'est cette dernière qui nous est indispensable.

Il faut remarquer qu'en général en ce qui concerne les tableaux, déterminer, sur base d'une étiquette, le contenu du tiroir concerné est un jeu d'enfant : c'est à cela que servent les [ ]. La correspondance inverse est nettement plus malaisée : connaissant un contenu possible de tiroir, déterminer l'étiquette correspondante demande une exploration de tous les tiroirs jusqu'à ce qu'on ait trouvé "le bon".

Si, face à une étagère, je vous demande ce que contient le tiroir marqué 4, il vous suffit d'ouvrir ce dernier pour me répondre. Si je vous affirme que l'un des tiroirs contient (peut-être) un trésor et que je vous demande lequel (= quelle est l'étiquette du tiroir au trésor), il va vous falloir les ouvrir tous, l'un après l'autre, jusqu'à avoir trouvé le bon.

Ce qu'il faudrait donc c'est en quelque sorte, dans le tableau ci-dessus, inverser le rôle des étiquettes et des contenus de tiroirs :

Valeurs

I V X L C D M

1 5 10 50 100 500 1000

et même, puisque les contenus des tiroirs peuvent bien entendu être des caractères :

Valeurs

'I' 'V' 'X' 'L' 'C' 'D' 'M'

1 5 10 50 100 500 1000

Mais, une fois de plus, les étiquettes ne sont pas adéquates : elles ne constituent pas un intervalle dans les entiers, mais quelques entiers épars : 1, 5, 10, 50, ...

Ce qu'il faudrait, c'est des étiquettes convenables, mais qui soient en correspondance avec 1, 5, 10, 50,... Cette correspondance est immédiate (même si elle peut paraître à certains, qui n'ont plus que de lointains souvenirs d'arithmétique, un peu "tirée par les cheveux").

unité → 1 = 10 0 = 10 0/2 étiquette : 0 contenu du tiroir : 'I' 5 unités étiquette : 1 contenu du tiroir : 'V' dizaine → 10 = 10 1 = 10 2/2 étiquette : 2 contenu du tiroir : 'X' 5 dizaines étiquette : 3 contenu du tiroir : 'L' centaine → 100 = 10 2 = 10 4/2 étiquette : 4 contenu du tiroir : 'C' 5 centaines étiquette : 5 contenu du tiroir : 'D' millier → 1000 = 10 3 = 10 6/2 étiquette : 6 contenu du tiroir : 'M'

Le tableau utilisé serait donc finalement :

V

'I' 'V' 'X' 'L' 'C' 'D' 'M'

0 1 2 3 4 5 6

ce qui se traduira en Pascal par : V : array[0..6] of char

Mais il faudra donc "jouer" avec la correspondance entre étiquettes et puissances de 10, comme ci-dessus.

1.8.3.2 Marche à suivre

V[0] ← 'I' V[1] ← 'V' V[2] ← 'X' V[3] ← 'L'

V[4] ← 'C' V[5] ← 'D' V[6] ← 'M' (1) Romain ' ' (2)

Si AuDelaDe1000 alors

Signe ←'°' Signe de type string[1]

sinon

Signe ← ' ' (2)

C ← 6 C de type intervalle entier -2..6 (9)

Tant que C ≥ 0

CALCULER_10_EXP_C_DIV_2 et le placer dans Expo Expo de type integer

N Habituel div Expo (3) N de type integer

Habituel Habituel mod Expo (4)

Au cas où N vaut

1 : Romain Romain + V[C] + Signe (5)

2 : Romain Romain + V[C] + Signe + V[C] + Signe

3 : Romain Romain + V[C] + Signe + V[C] + Signe + V[C] + Signe 4 : Romain Romain + V[C] + Signe + V[C+1] + Signe (6)

5 : Romain Romain + V[C+1] + Signe

6 : Romain Romain + V[C+1] + Signe + V[C] + Signe

7 : Romain Romain + V[C+1] + Signe + V[C] + Signe + V[C] + Signe

8 : Romain Romain + V[C+1] + Signe + V[C] + Signe + V[C] + Signe + V[C] + Signe 9 : Romain Romain + V[C] + Signe + V[C+2] + Signe (7)

C C - 2 (8)

Le coeur de cette marche à suivre, c'est une boucle, commandée par un compteur C qui passera par les valeurs 6, 4, 2, puis 0 (-2 provoquant l'arrêt). A chaque passage, la valeur de C

donnera d'une part une puissance de 10 (en réalité 10C div 2 donc 103, puis 102, puis 101, puis 100) et d'autre part, en consultant le tiroir d'étiquette C de V, le symbole romain

correspondant.

Quelques remarques supplémentaires :

(1) C'est par souci d'économie de place que les actions d'affectation destinées à garnir le tableau

V sont placés côte à côte plutôt que l'une en dessous de l'autre.

(2) D'une part, le paramètre Romain qui finira par contenir le nombre romain à obtenir, est initialisé par une chaîne vide; d'autre part, c'est également la chaîne vide qui est placée dans

Signe lorsque AuDelaDe1000 est faux.

(3) Expo contenant successivement 103, puis 102 puis 101, puis 100, N contiendra successivement le chiffre des milliers, puis, au tour suivant de la boucle Tant que ..., celui des centaines, puis celui des dizaines et enfin celui des unités.

(4) Conjointement, Habituel, qui contenait primitivement le nombre à transformer, contiendra les restes successifs de la division par les mêmes puissances de 10.

Ainsi, si Habituel valait primitivement 3698, on aura successivement : Expo : 1000 N : 3 Habituel : 698

Expo : 100 N : 6 Habituel : 98 Expo : 10 N : 9 Habituel : 8 Expo : 1 N : 8 Habituel : 0

Semblablement, si Habituel valait primitivement 302, on aura successivement : Expo : 1000 N : 0 Habituel : 302

Expo : 100 N : 3 Habituel : 2 Expo : 10 N : 0 Habituel : 2 Expo : 1 N : 2 Habituel : 0

(5) V[C] est le symbole romain à adjoindre à droite de ce que Romain contient déjà. Il est suivi de Signe (qui contient soit ° soit la chaîne vide).

(6) Lorsque N, contenant le chiffre concerné, vaut 4, les symboles à adjoindre à Romain, sont, non pas 4 fois V[C], mais V[C] suivi de V[C+1] (comme dans IV ou XL ou CD).

(7) Semblablement, lorsque N, contenant le chiffre concerné, vaut 9, les symboles à adjoindre à

Romain, sont, non pas 9 fois V[C], mais V[C] suivi de V[C+2] (comme dans IX ou XC ou CM).

(8) Le compteur gouvernant la boucle ne peut passer que par les valeurs 6, 4, 2 et 0 (et -2 qui provoquera l'arrêt). C'est pour cette raison qu'on écrit C - 2 et qu'on n'a pas utilisé une boucle "Pour...".

(9) Il faut noter que lorsque la boucle Tant que... s'interrompra, c'est parce que C aura pris la valeur -2. Si on souhaite donner à C une valeur de type intervalle, il doit donc s'agir de l'intervalle -2..6 et non 0..6.

Il faut remarquer aussi que si Habituel est nul, N reste constamment nul dans chaque itération de la boucle Tant que... et aucune des alternatives rencontrées dans la structure Au cas où... n'est rencontrée. Romain reste donc vide (comme primitivement défini).

Il reste, pour être complet, à spécifier l'action complexe CALCULER_10_EXP_C_DIV_2 : CALCULER_10_EXP_C_DIV_2

C

entier entre 0 et 6 (de type -2..6) Placer dans la variable Expo, l'entier 10 C div 2 Expo de type entier avec

Avant Après

- C, de type entier entre -2 et 6 (-2..6). Ses valeurs seront 6, 4 2 et 0.

- C est inchangé

- Expo contient 10C div 2.

Nous voici donc en mesure d'écrire le texte de la procédure ROMANISER_TOUT_ OU_PARTIE.

1.8.4 ROMANISER_TOUT_OU_PARTIE. Comment dire ? procedure ROMANISER_TOUT_OU_PARTIE(Habituel : LongInt;

AuDelaDe1000:boolean; var Romain : NombreRomain);

(* Elle fait transformer le nombre décimal Habituel (compris entre 0 et 3999) en son équivalent en chiffres romains soit Romain. Lorsque le paramètre AuDelaDe1000 est vrai, les chiffres romains sont assortis du symbole ° pour rappeler qu'ils multiplient 1000 *)

var V : array[0..6] of char;

(* la composante 0 contiendra le caractère I, la composante 1 le caractère V,...*)

Signe : string[1];

(* qui d'après la valeur de AuDelaDe1000, sera '°' ou la chaîne vide *)

C : -2..6;

(* compteur pour le "parcours" de Habituel *)

N,

(* qui contiendra les quotients des divisions de Habituel par 1000, 100,... *)

Expo

(* qui contiendra les puissances de 10 intervenant dans les divisions de Habituel *) (* ON NE CONNAIT PAS ENCORE ICI LE CONCEPT DE FONCTION *)

procedure CALCULER_10_EXP_C_DIV_2;

(* Elle fournit dans la variable Expo la valeur 10 exposant(C div 2) *)

begin (* début de ROMANISER_TOUT_OU_PARTIE *)

V[0]:='I'; V[1]:='V'; V[2]:='X'; V[3]:='L'; V[4]:='C'; V[5]:='D'; V[6]:='M';

(* au début la chaîne Romain doit être vide : on lui adjoindra divers caractères au fur et à mesure de l'exploration de Habituel *)

Romain:='';

(* choix du symbole à faire figurer auprès des chiffres romains (normaux ou à multiplier par 1000, lorsqu'ils sont accompagnés de ° *)

if AuDelaDe1000 then Signe:='°' else Signe:=''; C:=6; while C>=0 do begin CALCULER_10_EXP_C_DIV_2;

(* Expo contient 10 exposant (C div 2) : 1000, puis 100, ... puis 1 *)

N:=Habituel div Expo; (* N sera le chiffre des milliers, puis des centaines, ... *)

Habituel:=Habituel mod Expo; case N of 1 : Romain:=Romain+V[C]+Signe; 2 : Romain:=Romain+V[C]+Signe+V[C]+Signe; 3 : Romain:=Romain+V[C]+Signe+V[C]+Signe++V[C]+Signe; 4 : Romain:=Romain+V[C]+Signe+V[C+1]+Signe; 5 : Romain:=Romain+V[C+1]+Signe; 6 : Romain:=Romain+V[C+1]+Signe+V[C]+Signe; 7 : Romain:=Romain+V[C+1]+Signe+V[C]+Signe+V[C]+Signe;

8 : Romain:= Romain+V[C+1]+Signe+ V[C]+Signe+V[C]+ Signe+ V[C]+Signe; 9 : Romain:=Romain+V[C]+Signe+V[C+2]+Signe; end; C:=C-2; end; end;