3 Les limites de l'approche descendante
1. Une calculatrice pour le grand Jules
1.4 La calculatrice : Comment faire faire (à un premier niveau) ?
Il nous reste à mettre en oeuvre dans un choix de variables et dans l'écriture d'une marche à suivre la stratégie retenue.
1.4.1 Structure de données.
1.4.1.1 Constantes inhérentes au problème.
A ce niveau de l'analyse, la seule constante qu'on pourrait éventuellement mettre au jour est la taille maximale des chaînes de caractères qui constitueront les nombres romains. Ce n'est certes pas une constante importante pour la compréhension du problème.
1.4.1.2 Types
Comme Pascal nous le permettra, nous définirons le type NombreRomain comme synonyme de string[40]. Autrement dit, les nombres romains manipulés seront toujours des chaînes d'au plus 40 caractères.
On verra dans la suite que cette définition d'un type, qui paraît assez facultative, s'avérera indispensable, pour des raisons qui tiennent à la syntaxe exigée par Pascal.
1.4.1.3 Les tableaux nécessaires
Il n'y en a aucun à ce niveau de l'analyse.
A nouveau, il ne faut pas anticiper sur les actions (complexes !) qui resteront à analyser : où nous en sommes, aucun tableau n'est indispensable.
1.4.2 Marche à suivre
AVERTIR Répéter
Lis PremierNombreRomain PremierNombreRomain de type NombreRomain
Lis SecondNombreRomain SecondNombreRomain de type NombreRomain
DECIMALISER_PREMIERNOMBREROMAIN_EN_PREMIERNOMBREDECIMAL
PremierNombreDecimal de type entier long
DECIMALISER_SECONDNOMBREROMAIN_EN_SECONDNOMBREDECIMAL
SecondNombreDecimal de type entier long SommeDecimal ← PremierNombreDecimal + SecondNombreDecimal SommeDecimal entier long DifferenceDecimal ← PremierNombreDecimal SecondNombreDecimal DifferenceDecimal entier long ProduitDecimal ← PremierNombreDecimal * SecondNombreDecimal ProduitDecimal entier long
ROMANISER_SOMMEDECIMAL_EN_ SOMMEROMAIN
SommeRomain de type NombreRomain
ROMANISER_ABS_DIFFERENCEDECIMAL_EN_ DIFFERENCEROMAIN
DifferenceRomain de type NombreRomain
ROMANISER_PRODUITDECIMAL_EN_ PRODUITROMAIN
ProduitRomain de type NombreRomain
Affiche SommeRomain
Si DifferenceDecimal = 0 alors Affiche "Différence nulle" sinon
Si DifferenceDecimal > 0 alors Affiche DifferenceRomain
sinon
Affiche "-" suivi de DifferenceRomain Affiche ProduitRomain
Demander à l'utilisateur s'il souhaite recommencer et lire Reponse
jusqu'à ce que Reponse= 'N' Reponse de type char
Les équivalents décimaux des nombres romains traités ont tous le type entier long. Nous savons que le type entier simple (integer) ne couvre que les nombres entiers dans l'intervalle -32768 à 32767 et que, si cela est suffisant en ce qui concerne les nombres fournis (compris entre 1 et 999) comme en ce qui concerne la somme et la différence de ceux-ci, leur produit par contre posera des problèmes puisqu'il peut atteindre 998001(soit 999 x 999). Pascal offre pour la manipulation des entiers de grande taille le type "entier long" (LongInt) qui permet la représentation d'entiers entre -2147483648 et 2147483647 (voir page 29). Même si ce n'est indispensable que dans le cas du produit, j'ai préféré, pour des motifs de simplicité et d'homogénéité, déclarer tous les entiers traités de type entier long.
En laissant de côté, les spécifications de AVERTIR (pour lesquelles il suffit de consulter le premier écran proposé), il reste à préciser :
DECIMALISER_PREMIERNOMBREROMAIN_EN_PREMIERNOMBREDECIMAL
PremierNombreRomain →
de type NombreRomain
Transformer le nombre en chiffres romains
PremierNombreRomain en son équivalent décimal
et placer celui-ci dans PremierNombreDecimal
→ Premier NombreDecimal de type entier long avec
Avant Après
- PremierNombreRomain de type NombreRomain qui contient un nombre romain correctement écrit (entre I (1) et CMXCIX (999)).
- PremierNombreRomain ne peut avoir changé
- PremierNombreDecimal de type entier long, contient un entier entre 1 et 999 correspondant au nombre
PremierNombreRomain .
et, bien entendu :
DECIMALISER_SECONDNOMBREROMAIN_EN_SECONDNOMBREDECIMAL
SecondNombreRomain →
de type NombreRomain
Transformer le nombre en chiffres romains
SecondNombreRomain en son équivalent décimal et
placer ce dernier dans SecondNombreDecimal
→ SecondNombreDecimal de type entier long avec
Avant Après
- SecondNombreRomain de type NombreRomain qui contient un nombre romain correctement écrit (entre I (1) et CMXCIX (999)).
- SecondNombreRomain ne peut avoir changé
- SecondNombreDecimal de type entier long, contient un entier entre 1 et 999 correspondant au nombre
SecondNombreRomain .
et
ROMANISER_SOMMEDECIMAL_EN_ SOMMEROMAIN
SommeDecimal →
de type entier long
Transformer le résultat SommeDecimal en son
équivalent en chiffres romains SommeRomain → SommeRomain
de type NombreRomain avec
Avant Après
- SommeDecimal de type entier long, qui contient la somme écrite sous forme décimale, toujours comprise entre 2 et 1998.
- SommeDecimal ne peut avoir changé
- SommeRomain de type NombreRomain, contenant le nombre en chiffres romains correspondant à
SommeDecimal.
ROMANISER_ABS_DIFFERENCEDECIMAL_EN_ DIFFERENCEROMAIN
DifferenceDecimal →
de type entier long
Transformer la valeur absolue du résultat
DifferenceDecimal en son équivalent en chiffres romains DifferenceRomain
→ DifferenceRomain
de type NombreRomain avec
Avant Après
- DifferenceDecimal de type entier long, qui contient la différence écrite sous forme décimale, toujours comprise entre -998 et 998.
- DifferenceDecimal ne peut avoir changé
- DifferenceRomain de type NombreRomain, contenant le nombre en chiffres romains correspondant à la valeur absolue de DifferenceDecimal.
ROMANISER_PRODUITDECIMAL_EN_ PRODUITROMAIN
ProduitDecimal → de type entier long
Transformer le résultat ProduitDecimal en son
équivalent en chiffres romains ProduitRomain → ProduitRomain
de type NombreRomain avec
Avant Après - ProduitDecimal de type entier long, qui contient le produit écrit
sous forme décimale, toujours compris entre 1 et 998001 (999*999)
- ProduitDecimal ne peut avoir changé
- ProduitRomain de type NombreRomain, contenant le nombre en chiffres romains correspondant à ProduitDecimal.
On pourrait poursuivre l'analyse comme si de rien n'était avec chacune des actions complexes ainsi spécifiée. On constate cependant que les deux premières actions complexes
DECIMALISER_PREMIERNOMBREROMAIN_EN_PREMIERNOMBREDECIMAL et DECIMALISER_SECONDNOMBREROMAIN_EN_SECONDNOMBREDECIMAL constituent
la même action, appliquée la première fois à PremierNombreRomain pour remplir PremierNombre
Decimal, la seconde fois à SecondNombreRomain pour remplir SecondNombreDecimal. Il serait
donc absurde de recommencer deux fois la même analyse, les seules modifications de l'une à l'autre étant les changements du nom des variables concernées.
Ce qu'il faudrait, c'est non pas pouvoir analyser l'action permettant, sur base de telle variable
précise contenant un nombre en chiffres romains de passer à telle variable précise contenant son
équivalent décimal, mais décrire l'action qui sur base d'un nombre en chiffres romains fournisse dans une quelconque variable (de type entier long) son équivalent décimal. C'est seulement aux moments où cette action serait activée (= lors de l'appel de la procédure) qu'on préciserait à chaque fois le nombre à transformer et la variable accueillant le résultat de la transformation.
On écrirait en quelque sorte :
DECIMALISER(Romain : NombreRomain; var Decimal : entier long)
Romain →
paramètre de type NombreRomain
Transformer le nombre en chiffres romains Romain en
son équivalent décimal et le placer dans Decimal → Decimal
paramètre de type entier long avec
Avant Après - Romain : paramètre de type NombreRomain qui contient un
nombre romain correctement écrit (entre I (1) et CMXCIX (999)).
- Decimal : paramètre de type entier long, contient un entier entre 1 et 999 correspondant au nombre en chiffres romains
Romain .
Au moment de l'appel des procédures, au lieu d'appeler successivement deux procédures différentes, c'est la même procédure, DECIMALISER, qui serait appelée, mais en précisant à chaque fois ce que sont vraiment chacun des deux paramètres dont elle est assortie.
Ainsi, au lieu d'appeler
DECIMALISER_PREMIERNOMBREROMAIN_EN_PREMIERNOMBREDECIMAL, on appellerait DECIMALISER, en précisant, pour cette fois que
- PremierNombreRomain "remplace" Romain et
- PremierNombreDecimal "remplace" Decimal
et au lieu de
DECIMALISER_SECONDNOMBREROMAIN_EN_SECONDNOMBREDECIMAL, on appellerait à nouveau DECIMALISER, en précisant, à ce second appel, que
- SecondNombreRomain "remplace" Romain et
- SecondNombreDecimal "remplace" Decimal.
Romain et Decimal sont ce que nous appellerons des paramètres de la procédure
DECIMALISER. Ils sont encadrés dans le schéma proposé pour insister sur le fait qu'ils font partie
de la procédure spécifiée. A chaque appel de DECIMALISER, ces deux paramètres se verront
nouveau et important, mais il est indispensable d'avoir compris dès à présent à quoi ces paramètres sont utiles.
Il est inutile, pour l'instant, de se préoccuper des détails syntaxiques. Ainsi, le fait que le paramètre Decimal est précédé de la mention var ne sera expliqué que plus loin (voir page 159). De la même manière, les trois dernières actions complexes peuvent se résumer en une seule :
ROMANISER(Decimal : entier long; var Romain : NombreRomain)
Decimal →
de type entier long
Transformer Decimal en son équivalent en chiffres
romains Romain → Romain
de type NombreRomain avec
Avant l'exécution de ROMANISER Après l'exécution de ROMANISER - Decimal paramètre de type entier long qui contient un nombre
entier entre 0 et 998001.
- Romain, paramètre de type NombreRomain, contenant le nombre en chiffres romains correspondant à Decimal.
Il faut noter que ces deux paramètres Decimal et Romain accompagnant ROMANISER n'ont rien à voir (même s'ils portent des noms identiques) avec les deux paramètres Romain et
Decimal accompagnant DECIMALISER.
Et au moment des appels, on appellerait à trois reprises la même procédure ROMANISER, mais on préciserait :
- pour la transformation de la somme : - Decimal est SommeDecimal et
- Romain est à comprendre comme SommeRomain;
- en ce qui concerne la différence :
- Decimal est la valeur absolue de DifferenceDecimal;
- Romain est à comprendre comme DifferenceRomain;
Il faut bien noter que ROMANISER ayant comme tâche de transformer le nombre décimal qu'on lui passe (à travers le paramètre Decimal), il faudra, lors de l'appel destiné à "romaniser" la différence contenue dans DifferenceDecimal, remplacer le paramètre Decimal non par
DifferenceDecimal mais par la valeur absolue de cette dernière.
- en ce qui concerne le produit : - Decimal est ProduitDecimal et
- Romain est en réalité ProduitRomain.
Avec cette réécriture qui fait seulement apparaître deux procédures ROMANISER et DECIMALISER, assorties de paramètres, là où 5 procédures avaient primitivement été isolées, la marche à suivre principale devient :
AVERTIR Répéter
Lis PremierNombreRomain PremierNombreRomain,
Lis SecondNombreRomain SecondNombreRomain de type NombreRomain
DECIMALISER(PremierNombreRomain, PremierNombreDecimal)
PremierNombreDecimal de type entier long
DECIMALISER(SecondNombreRomain, SecondNombreDecimal)
SecondNombreDecimal de type entier long SommeDecimal ← PremierNombreDecimal + SecondNombreDecimal SommeDecimal de type entier long DifferenceDecimal ← PremierNombreDecimal -SecondNombreDecimal DifferenceDecimal entier long
ProduitDecimal ← PremierNombreDecimal * SecondNombreDecimal ProduitDecimal entier long
ROMANISER(SommeDecimal, SommeRomain)
SommeRomain de type NombreRomain
ROMANISER(abs(DifferenceDecimal), DifferenceRomain)
DifferenceRomain de type NombreRomain
ROMANISER(ProduitDecimal, ProduitRomain)
ProduitRomain de type NombreRomain
Affiche SommeRomain
Si DifferenceDecimal = 0 alors Affiche "Différence nulle" sinon
Si DifferenceDecimal > 0 alors Affiche DifferenceRomain
sinon
Affiche "-" suivi de DifferenceRomain Affiche ProduitRomain
Demander à l'utilisateur s'il souhaite recommencer et lire Reponse
jusqu'à ce que Reponse= 'N' Reponse de type caractère
On notera dès à présent que lors de l'appel des procédures assorties de paramètres, chacun des paramètres définis dans les spécifications (= la description du "Quoi faire ?") de ces procédures se voit associer une information ou une variable du programme principal. Il faut donc mettre en parallèle la définition de la procédure et de ses paramètres et son appel, qui précise les valeurs prises, lors de cet appel, par les paramètres. Ainsi, par exemple :
Définition : DECIMALISER( Romain Decimal )
1er appel :DECIMALISER(PremierNombreRomain,PremierNombreDecimal) Définition : DECIMALISER( Romain Decimal )
2ème appel : DECIMALISER(SecondNombreRomain,SecondNombreDecimal)