Accueil Retour au texte

Math::Roman avec des caractères Unicode

Voici un exemple de script utilisant Math::Roman version avancée pour générer les nombres comme indiqué par le W3C. En fait, ce que fait le programme, c'est de vérifier que Math::Roman et Convert::Number::Roman donnent bien le même résultat et que Math::Roman est capable de convertir dans les deux sens.

#!/usr/bin/perl
#
# Tests sur les modules de nombres romains
# Test exhaustif sur les caractères Unicode et la spécification W3C

use strict;
use warnings;
use Math::Roman;
use Convert::Number::Roman;
# U+2160 = o20540 = 8544, U+2170 = 0o20560 = 8560
Math::Roman::tokens( "\x{2160}",  1, "\x{2161}",  2, "\x{2162}",  3, "\x{2163}",  4,
                     "\x{2164}",  5, "\x{2165}",  6, "\x{2166}",  7, "\x{2167}",  8, "\x{2168}",  9,
                     "\x{2169}", 10, "\x{216A}", 11, "\x{216B}", 12, "\x{2169}\x{2162}", 13,
                     "\x{2169}\x{2163}", 14, "\x{2169}\x{2164}", 15, "\x{2169}\x{2165}", 16,
                     "\x{2169}\x{2166}", 17, "\x{2169}\x{2167}", 18, "\x{2169}\x{2168}", 19,
                     "\x{2169}\x{2169}",  20, "\x{2169}\x{216A}", 21, "\x{2169}\x{216B}", 22,
                     "\x{2169}\x{2169}\x{2162}", 23,
                     "\x{2169}\x{2169}\x{2163}", 24,
                     "\x{2169}\x{2169}\x{2164}", 25,
                     "\x{2169}\x{2169}\x{2165}", 26,
                     "\x{2169}\x{2169}\x{2166}", 27,
                     "\x{2169}\x{2169}\x{2167}", 28,
                     "\x{2169}\x{2169}\x{2168}", 29,
                     "\x{2169}\x{2169}\x{2169}", 30,
                     "\x{2169}\x{2169}\x{216A}", 31,
                     "\x{2169}\x{2169}\x{216B}", 32,
                     "\x{2169}\x{2169}\x{2169}\x{2162}", 33,
                     "\x{2169}\x{2169}\x{2169}\x{2163}", 34,
                     "\x{2169}\x{2169}\x{2169}\x{2164}", 35,
                     "\x{2169}\x{2169}\x{2169}\x{2165}", 36,
                     "\x{2169}\x{2169}\x{2169}\x{2166}", 37,
                     "\x{2169}\x{2169}\x{2169}\x{2167}", 38,
                     "\x{2169}\x{2169}\x{2169}\x{2168}", 39,
                     "\x{2169}\x{216C}",  40, "\x{216C}",  50, "\x{2169}\x{216D}",  90, "\x{216D}",  100,
                     "\x{216D}\x{216E}", 400, "\x{216E}", 500, "\x{216D}\x{216F}", 900, "\x{216F}", 1000);

for my $n (1..3999) {
  my $cnr = Convert::Number::Roman->new($n);
  my $mr  = Math::Roman->new($n);
  if ($cnr->convert ne $mr) {
    print "Erreur 1 pour $n\n";
  }
  if ($mr->as_number != $n) {
    print "Erreur 2 pour $n\n";
  }

}

Pour l'argument de tokens, l'idée d'origine était de simplement énumérer les caractères Unicode avec leur valeur de 1 à 12, puis de continuer avec 40, 50, 90 et ainsi de suite. Cela n'a pas fonctionné, car 13 a été converti en \x{216B}\x{2160}, soit "XII" + "I", alors qu'il aurait fallu \x{2169}\x{2162}, soit "X" + "III". En fait, avec un raisonnement de proche en proche, on peut se rendre compte qu'il faut spécifier tous les nombres jusqu'à 40, avant de pouvoir enfin laisser des trous.

À titre de comparaison, voici un script dans lequel j'ai choisi de ne pas utiliser \x{216A} pour "XI" et \x{216B} pour "XII". Le script compare avec le résultat de Number::Convert::Roman dans lequel je substitue "X" + "I" à "XI" et "X" + "II" à "XII". Comme vous pouvez le voir, c'est beaucoup plus simple et beaucoup plus concis.

#!/usr/bin/perl
#
# Tests sur les modules de nombres romains
# Test sur les caractères Unicode et la spécification W3C, en ignorant les caractères pour XI et XII

use strict;
use warnings;
use Math::Roman;
use Convert::Number::Roman;
# U+2160 = o20540 = 8544, U+2170 = 0o20560 = 8560
Math::Roman::tokens( "\x{2160}",  1, "\x{2161}",  2, "\x{2162}",  3, "\x{2163}",  4,
                     "\x{2164}",  5, "\x{2165}",  6, "\x{2166}",  7, "\x{2167}",  8, "\x{2168}",  9,
                     "\x{2169}",  10,
                     "\x{2169}\x{216C}",  40, "\x{216C}",  50, "\x{2169}\x{216D}",  90, "\x{216D}",  100,
                     "\x{216D}\x{216E}", 400, "\x{216E}", 500, "\x{216D}\x{216F}", 900, "\x{216F}", 1000);

for my $n (1..3999) {
  my $mod = $n % 50;
  my $cnr = Convert::Number::Roman->new($n);
  my $cnr_str = $cnr->convert;
  $cnr_str =~ s/\x{216A}/\x{2169}\x{2160}/g;
  $cnr_str =~ s/\x{216B}/\x{2169}\x{2161}/g;
  my $mr  = Math::Roman->new($n);
  if ($cnr_str ne $mr) {
    print "Erreur 1 pour $n\n";
  }
  if ($mr->as_number != $n) {
    print "Erreur 2 pour $n\n";
  }

}

Cela dit, les scripts permettent de générer et décoder tous les nombres valides, mais ils ne permettent pas de rejeter tous les nombres invalides. Je laisse cela au lecteur à titre de (pénible) exercice.

© 2011 Jean Forget et Les Mongueurs de Perl. Le code dans ce fichier est du logiciel libre. Vous pouvez le redistribuer ou le modifier sous les mêmes termes que Perl 5.10.0. Le texte est sous licence Creative Commons paternité, pas de modification.


Accueil Retour au texte