TP : Convertir un nombre en toutes lettres

Nous arrivons enfin au premier TP de ce cours ! Celui-ci a pour but de vous faire réviser et de mettre en pratique l’essentiel de ce que vous avez appris dans les chapitres précédents. Nous vous conseillons donc fortement de le lire et d’essayer de faire l’exercice proposé. Vous pourrez ainsi réviser l’utilisation des variables, conditions, boucles, fonctions et tableaux. Ce TP vous permettra aussi d’approfondir vos connaissances sur l’identification et la conversion des nombres.

Présentation de l’exercice

Ce TP sera consacré à un exercice bien particulier : la conversion d’un nombre en toutes lettres. Ainsi, si l’utilisateur saisit le nombre « 41 », le script devra retourner ce nombre en toutes lettres : « quarante et un ». Ne vous inquiétez pas, vous en êtes parfaitement capables ! Et nous allons même vous aider un peu avant de vous donner le corrigé.

La marche à suivre

Pour mener à bien votre exercice, voici quelles sont les étapes que votre script devra suivre (vous n’êtes pas obligés de faire comme ça, mais c’est conseillé).

  1. L’utilisateur est invité à entrer un nombre compris entre 0 et 999.

  2. Ce nombre doit être envoyé à une fonction qui se charge de le convertir en toutes lettres.

  3. Cette même fonction doit contenir un système permettant de séparer les centaines, les dizaines et les unités. Ainsi, la fonction doit être capable de voir que le nombre 365 contient trois centaines, six dizaines et cinq unités. Pour obtenir ce résultat, pensez à utiliser le modulo. Exemple : 365 % 10 = 5.

  4. Une fois le nombre découpé en trois chiffres, il ne reste plus qu’à convertir ces derniers en toutes lettres.

  5. Lorsque la fonction a fini de s’exécuter, elle renvoie le nombre en toutes lettres.

  6. Une fois le résultat de la fonction obtenu, il est affiché à l’utilisateur.

  7. Lorsque l’affichage du nombre en toutes lettres est terminé, on redemande un nouveau nombre à l’utilisateur.

L’orthographe des nombres

Nous allons ici écrire les nombres « à la française », c’est-à-dire la version la plus compliquée… Nous vous conseillons de faire de même, cela vous entraînera davantage que l’écriture belge ou suisse. D’ailleurs, puisque l’écriture des nombres en français est assez complexe, nous vous conseillons d’aller faire un petit tour sur cette page, afin de vous remémorer les principales règles : http://www.leconjugueur.com/frlesnombres.php .

À noter que vous n’êtes pas obligés de respecter toutes ces règles orthographiques. L’important est que votre code puisse afficher les nombres en toutes lettres, les fautes orthographiques sont secondaires.

Tester et convertir les nombres

Afin que vous puissiez avancer sans trop de problèmes dans la lecture de votre code, il va falloir étudier l’utilisation des fonctions  parseInt()etisNaN().

Retour sur la fonction parseInt()

Nous allons ici approfondir un peu l’utilisation deparseInt()étant donné que vous savez déjà vous en servir. Cette fonction possède en réalité non pas un mais deux arguments. Le second est très utile dans certains cas, comme celui-ci :

alert(parseInt('010')); // Affiche « 8 » sur certains navigateurs

Sur certains navigateurs, le chiffre affiché par ce code n’est pas 10 comme souhaité mais 8 ! La raison à cela est que la fonctionparseInt() supporte plusieurs bases arithmétiques . Nous pouvons ainsi lui dire que le premier argument est en binaire. La fonction retournera alors le nombre en base 10 (notre propre base de calcul, le système décimal) après avoir fait la conversion base 2 (système binaire) → base 10 (système décimal). Donc, si nous écrivons :

alert(parseInt('100', 2)); // Affiche « 4 »

nous obtenons bien le nombre 4 à partir de la base 2.

Mais tout à l’heure, le second argument n’était pas spécifié et pourtant nous avons eu une conversion aberrante. Pourquoi ? Tout simplement parce que si le second argument n’est pas spécifié, la fonctionparseInt()va tenter de trouver elle-même la base arithmétique du nombre que vous avez passé en premier argument. Ce comportement est stupide vu que la fonction se trompe très facilement. La preuve, notre premier exemple sans le second argument a interprété notre nombre comme étant en base 8 (système octal), simplement parce que la chaîne de caractères commence par un 0.

Pour convertir correctement notre premier nombre, nous devons donc indiquer àparseInt()  que ce dernier est en base 10, comme ceci :

alert(parseInt('010', 10)); // Affiche « 10 »

Nous obtenons bien le nombre 10 ! Rappelez-vous bien ce second argument, il vous servira pour le TP et, à n’en pas douter, dans une multitude de problèmes futurs !

La fonction isNaN()

Jusqu’à présent, pour tester si une variable était un nombre, vous utilisiez l’instruction  typeof  , qui n’est pas sans poser problème :

var test = parseInt('test'); // Contient au final la valeur « NaN »
alert(typeof test); // Affiche « number »

 

En effet, dans cet exemple, notre variable contient la valeur NaN (Not a Number) et pourtant l’instruction typeof renvoie number en guise de type. C’est assez contradictoire, non ? En fait, l’instructiontypeofest limitée pour tester les nombres. Il est préférable d’utiliser à la place la fonctionisNaN()(is Not a Number) :

var test = parseInt('test'); // Contient au final la valeur « NaN »
alert(isNaN(test)); // Affiche « true »

 

Pourquoi obtenons-noustrue? Tout simplement parce queisNaN()renvoietruequand la variable n’est pas un nombre, et false dans le cas contraire.

Il est temps de se lancer !

Vous voilà maintenant prêts à vous lancer dans l’écriture de votre code. Nous précisons de nouveau que les nombres à convertir vont de 0 à 999, mais rien ne vous empêche de faire plus si le cœur vous en dit. Évitez de faire moins, vous manqueriez une belle occasion de vous entraîner correctement.

Bon courage !

Correction

Nous espérons que vous avez réussi à obtenir quelque chose d’intéressant, le sujet est certes un peu tordu, mais vos connaissances sont largement suffisantes pour pouvoir le réaliser.

Toutefois, si vous rencontrez un blocage alors que vous avez bien compris ce qui a été dit dans les chapitres précédents, ne vous inquiétez pas. La programmation est un domaine où la logique règne en maîtresse (bon, d’accord, pas tout le temps !), il faut donc de l’expérience pour en arriver à développer de façon logique. Si vous n’avez pas réussi à coder l’exercice aujourd’hui, ce n’est pas un drame ! Faites une pause, essayez de faire des exercices plus simples et revenez ensuite sur celui-ci. Si vous arrivez à lire et comprendre le corrigé suivant, alors vous êtes capables de réaliser cet exercice tout aussi bien que nous, voire mieux !

Le corrigé complet

Précisons tout d’abord que ce code n’est pas universel. Il existe de nombreuses autres façons de coder cet exercice et cette version n’est pas forcément la meilleure. Si vous cherchez à recoder cet exercice après avoir lu le corrigé, ne refaites pas exactement la même chose ! Inventez votre propre solution, innovez selon vos propres idées ! Après tout, on dit qu’il existe autant d’algorithmes que de personnes dans le monde, car chacun possède sa propre façon de penser, alors vous devriez pouvoir réaliser une autre version de ce code en réfléchissant par vous-mêmes !

function num2Letters(number) {
if (isNaN(number) || number < 0 || 999 < number) {
return 'Veuillez entrer un nombre entier compris entre 0 et 999.';
}
var units2Letters = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf'],
tens2Letters = ['', 'dix', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante', 'soixante', 'quatre-vingt', 'quatre-vingt'];
var units = number % 10,
tens = (number % 100 - units) / 10,
hundreds = (number % 1000 - number % 100) / 100;
var unitsOut, tensOut, hundredsOut;
if (number === 0) {
return 'zéro';
} else {
// Traitement des unités
unitsOut = (units === 1 && tens > 0 && tens !== 8 ? 'et-' : '') + units2Letters[units];
// Traitement des dizaines
if (tens === 1 && units > 0) {
tensOut = units2Letters[10 + units];
unitsOut = '';
} else if (tens === 7 || tens === 9) {
tensOut = tens2Letters[tens] + '-' + (tens === 7 && units === 1 ? 'et-' : '') + units2Letters[10 + units];
unitsOut = '';
} else {
tensOut = tens2Letters[tens];
}
tensOut += (units === 0 && tens === 8 ? 's' : '');
// Traitement des centaines
hundredsOut = (hundreds > 1 ? units2Letters[hundreds] + '-' : '') + (hundreds > 0 ? 'cent' : '') + (hundreds > 1 && tens == 0 && units == 0 ? 's' : '');
// Retour du total
return hundredsOut + (hundredsOut && tensOut ? '-': '') + tensOut + (hundredsOut && unitsOut || tensOut && unitsOut ? '-': '') + unitsOut;
}
}
var userEntry;
while (userEntry = prompt('Indiquez le nombre à écrire en toutes lettres (entre 0 et 999) :')) {
alert(num2Letters(parseInt(userEntry, 10)));
}

Essayer le code

Les explications

Nous allons maintenant analyser ce corrigé afin que vous le compreniez bien.

Le « squelette » du code

Un code doit toujours posséder ce qui peut être appelé un « squelette » autour duquel il peut s’articuler, c’est-à-dire un code contenant les principales structures de votre script (comme un objet, une boucle, une fonction, etc.) que nous allons pouvoir étoffer au fur et à mesure. Dans notre cas, nous avons besoin d’une fonction qui fera la conversion des nombres, ainsi que d’une boucle pour demander à l’utilisateur d’entrer un nouveau nombre sans jamais s’arrêter (sauf si l’utilisateur le demande). Voici ce que ça donne :

function num2Letters(number) { // « num2Letters » se lit « number to letters », le « 2 » est une abréviation souvent utilisée en programmation
// Notre fonction qui s'occupera de la conversion du nombre. Elle possède un argument lui permettant de recevoir les nombres en question.
}
var userEntry; // Contient le texte entré par l'utilisateur
while (userEntry = prompt('Indiquez le nombre à écrire en toutes lettres (entre 0 et 999) :')) {
/*
Dans la condition de la boucle, on stocke le texte de l'utilisateur dans la variable « userEntry ».
Ce qui fait que si l'utilisateur n'a rien entré (valeur nulle, donc équivalente à false) la condition ne sera pas validée.
Donc la boucle while ne s'exécutera pas et dans le cas contraire la boucle s'exécutera.
*/
}

 L’appel de la fonction de conversion

Notre boucle fonctionne ainsi : on demande un nombre à l’utilisateur, lequel est ensuite envoyé à la fonction de conversion. Voici comment procéder :

while (userEntry = prompt('Indiquez le nombre à écrire en toutes lettres (entre 0 et 999) :')) {
/*
On « parse » (en base 10) la chaîne de caractères de l'utilisateur pour l'envoyer ensuite
à notre fonction de conversion qui renverra le résultat à la fonction alert().
*/
alert(num2Letters(parseInt(userEntry, 10)));
}

Initialisation de la fonction de conversion

Le squelette de notre code est prêt et la boucle est complète. Il ne nous reste plus qu’à créer la fonction, c’est-à-dire le plus gros du travail. Pour vous expliquer son fonctionnement, nous allons la découper en plusieurs parties. Nous allons ici nous intéresser à l’initialisation, qui concerne la vérification de l’argument number et la déclaration des variables nécessaires au bon fonctionnement de notre fonction.

Tout d’abord, nous devons vérifier l’argument reçu :

function num2Letters(number) {
if (isNaN(number) || number < 0 || 999 < number) { // Si l'argument n'est pas un nombre (isNaN), ou si le nombre est inférieur à 0, ou s'il est supérieur à 999
return 'Veuillez entrer un nombre entier compris entre 0 et 999.'; // Alors on retourne un message d'avertissement
}
}

Puis, nous déclarons les variables nécessaires à la bonne exécution de notre fonction :

function num2Letters(number) {
// Code de vérification de l'argument […]
/*
Ci-dessous on déclare deux tableaux contenant les nombres en toutes lettres, un tableau pour les unités et un autre pour les dizaines. Le tableau des
unités va du chiffre 1 à 19 afin de simplifier quelques opérations lors de la conversion du nombre en lettres. Vous comprendrez ce système par la suite.
*/
var units2Letters = ['', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf'],
tens2Letters = ['', 'dix', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante', 'soixante', 'quatre-vingt', 'quatre-vingt'];
/*
Ci-dessous on calcule le nombre d'unités, de dizaines et de centaines à l'aide du modulo.
Le principe est simple : si on prend 365 % 10 on obtient le reste de la division par 10 qui est : 5. Voilà les unités.
Ensuite, sur 365 % 100 on obtient 65, on soustrait les unités à ce nombre 65 - 5 = 60, et on divise par 10 pour obtenir 6, voilà les dizaines !
Le principe est le même pour les centaines sauf qu'on ne soustrait pas seulement les unités mais aussi les dizaines.
*/
var units = number % 10,
tens = (number % 100 - units) / 10,
hundreds = (number % 1000 - number % 100) / 100;
// Et enfin on crée les trois variables qui contiendront les unités, les dizaines et les centaines en toutes lettres.
var unitsOut, tensOut, hundredsOut;
}

Conversion du nombre en toutes lettres

Maintenant que notre fonction est entièrement initialisée, il ne nous reste plus qu’à attaquer le cœur de notre script : la conversion. Comment allons-nous procéder ? Nous avons les unités, dizaines et centaines dans trois variables séparées ainsi que deux tableaux contenant les nombres en toutes lettres. Toutes ces variables vont nous permettre de nous simplifier la vie dans notre code. Par exemple, si l’on souhaite obtenir les unités en toutes lettres, il ne nous reste qu’à faire ceci :

unitsOut = units2Letters[units];

Si ça paraît aussi simple, c’est parce que notre code a été bien pensé dès le début et organisé de façon à pouvoir travailler le plus facilement possible. Il y a sûrement moyen de faire mieux, mais ce code simplifie quand même grandement les choses, non ? Maintenant, notre plus grande difficulté va être de se plier aux règles orthographiques de la langue française !

Vous aurez sans doute remarqué que nos tableaux ne contiennent pas le chiffre « zéro ». Nous allons l’ajouter à l’aide d’une condition :

function num2Letters(number) {
// Code de vérification de l'argument […]
// Code d'initialisation […]
if (number === 0) {
return 'zéro'; // Tout simplement ! Si le nombre vaut « 0 » alors on retourne « zéro », normal !
}
}

Occupons-nous à présent des unités :

function num2Letters(number) {
// Code de vérification de l'argument […]
// Code d'initialisation […]
if (number === 0) {
return 'zéro'; // Tout simplement ! Si le nombre vaut « 0 » alors on retourne « zéro », normal !
} else { // Si « number » est différent de « 0 » alors on lance la conversion complète du nombre
/*
Ci-dessous on calcule les unités. La dernière partie du code (après le +) ne doit normalement pas vous poser de problèmes. Mais pour la
condition ternaire je pense que vous voyez assez peu son utilité. En fait, elle va permettre d'ajouter « et- » à l'unité quand cette dernière
vaudra 1 et que les dizaines seront supérieures à 0 et différentes de 8. Pourquoi ? Tout simplement parce qu'on ne dit pas « vingt-un »
mais « vingt-et-un »$$$ vingt et un ? à revoir $$$, cette règle s'applique à toutes les dizaines sauf à « quatre-vingt-un » (d'où le « tens !== 8 »).
*/
unitsOut = (units === 1 && tens > 0 && tens !== 8 ? 'et-' : '') + units2Letters[units];
}
}

Viennent ensuite les dizaines. Attention, ça se complique pas mal !

function num2Letters(number) {
// Code de vérification de l'argument […]
// Code d'initialisation […]
if (number === 0) {
return 'zéro'; // Tout simplement ! Si le nombre vaut « 0 » alors on retourne « zéro », normal !
} else { // Si « number » est différent de « 0 » alors on lance la conversion complète du nombre
// Conversion des unités […]
/*
La condition qui suit se charge de convertir les nombres allant de 11 à 19. Pourquoi cette tranche de nombres ?
Parce qu'ils ne peuvent pas se décomposer (essayez de décomposer en toutes lettres le nombre « treize », vous nous
en direz des nouvelles), ils nécessitent donc d'être mis un peu à part. Bref, leur conversion en lettres
s'effectue donc dans la partie concernant les dizaines. Ainsi,on va se retrouver avec, par exemple,
« tensOut = 'treize'; » donc au final, on va effacer la variable « unitsOut » vu qu'elle ne servira à rien.
*/
if (tens === 1 && units > 0) {
tensOut = units2Letters[10 + units]; // Avec « 10 + units » on obtient le nombre souhaité entre 11 et 19
unitsOut = ''; // Cette variable ne sert plus à rien, on la vide
}
/*
La condition suivante va s'occuper des dizaines égales à 7 ou 9. Pourquoi ? Eh bien un peu pour la même raison que la
précédente condition : on retrouve les nombres allant de 11 à 19. En effet, on dit bien « soixante-treize » et
« quatre-vingt-treize » et non pas « soixante-dix-trois » ou autre bêtise du genre. Bref, cette condition est donc chargée,
elle aussi, de convertir les dizaines et les unités. Elle est aussi chargée d'ajouter la conjonction « et- » si l'unité
vaut 1, car on dit « soixante-et-onze » et non pas « soixante-onze ».
*/
else if (tens === 7 || tens === 9) {
tensOut = tens2Letters[tens] +'-'+ (tens === 7 && units === 1 ? 'et-' : '') + units2Letters[10 + units];
unitsOut = ''; // Cette variable ne sert plus à rien ici non plus, on la vide
}
// Et enfin la condition « else » s'occupe des dizaines qu'on peut qualifier de « normales ».
else {
tensOut = tens2Letters[tens];
}
// Dernière étape, « quatre-vingt » sans unité prend un « s » à la fin : « quatre-vingts »
tensOut += (units === 0 && tens === 8 ? 's' : '');
}
}

Un peu complexe tout ça, n’est-ce pas ? Rassurez-vous, c’était le passage le plus difficile. Attaquons-nous maintenant aux centaines, plus simples :

function num2Letters(number) {
// Code de vérification de l'argument […]
// Code d'initialisation […]
if (number === 0) {
return 'zéro'; // Tout simplement ! Si le nombre vaut « 0 » alors on retourne « zéro », normal !
} else { // Si « number » est différent de « 0 » alors on lance la conversion complète du nombre
// Conversion des unités […]
// Conversion des dizaines […]
/*
La conversion des centaines se fait en une ligne et trois ternaires. Ces trois ternaires se chargent d'afficher un
chiffre si nécessaire avant d'écrire « cent » (exemple : « trois-cents »), d'afficher ou non la chaîne « cent » (s’il
n'y a pas de centaines, on ne l'affiche pas, normal), et enfin d'ajouter un « s » à la chaîne « cent » s’il n'y a ni
dizaines, ni unités et que les centaines sont supérieures à 1.
*/
hundredsOut = (hundreds > 1 ? units2Letters[hundreds] + '-' : '') + (hundreds > 0 ? 'cent' : '') + (hundreds > 1 && tens == 0 && units == 0 ? 's' : '');
}
}

Retour de la valeur finale

Une fois que le système de conversion est terminé, il ne nous reste plus qu’à retourner toutes ces valeurs concaténées les unes aux autres avec un tiret.

function num2Letters(number) {
// Code de vérification de l'argument […]
// Code d'initialisation […]
if (number === 0) {
return 'zéro'; // Tout simplement ! Si le nombre vaut « 0 » alors on retourne « zéro », normal !
} else { // Si « number » est différent de « 0 » alors on lance la conversion complète du nombre
// Conversion des unités […]
// Conversion des dizaines […]
// Conversion des centaines […]
/*
Cette ligne de code retourne toutes les valeurs converties en les concaténant les unes aux autres avec un tiret. Pourquoi y a-t-il
besoin de ternaires ? Parce que si l’on n'en met pas, alors on risque de retourner des valeurs du genre « -quatre-vingt- » juste parce
qu'il n'y avait pas de centaines et d'unités.
*/
return hundredsOut + (hundredsOut && tensOut ? '-': '') + tensOut + (hundredsOut && unitsOut || tensOut && unitsOut ? '-': '') + unitsOut;
}
}

Conclusion

Si vous avez trouvé cet exercice difficile, rassurez-vous : il l’est. Il existe certes des codes beaucoup plus évolués et compliqués, mais pour quelqu’un qui débute c’est déjà beaucoup ! Si vous n’avez toujours pas tout compris, je ne peux que vous encourager à relire les explications ou refaire l’exercice. Si, malgré tout, vous n’y arrivez pas, consultez le Site du Zéro, dont le forum possède une rubrique consacrée au JavaScript. Vous y trouverez facilement de l’aide, pour peu que vous parveniez à expliquer correctement votre problème.