[Melbourne-pm] Roman numerals & assessing a CPAN module
Damian Conway
damian at conway.org
Thu Dec 10 20:10:51 PST 2009
Just in case no module proves acceptable, here's some raw code extracted
from Lingua::Romana::Perligata. It handles Roman numerals up to 1 billion.
As you see, for larger numbers it uses parens instead of the C-I-reversed-C
of classical Latin. I guess under Unicode, you could represent such
numbers more accurately with the codepoints:
0x216D (ROMAN NUMERAL ONE HUNDRED),
0x2160 (ROMAN NUMERAL ONE),
0x2183 (ROMAN NUMERAL REVERSED ONE HUNDRED)
Damian
-----cut----------cut----------cut----------cut----------cut-----
sub make_range {
my ($unit, $five, $ten) = @_;
my ($two, $three) = ($unit x 2, $unit x 3);
return [
"", $unit, $two, $three, $unit.$five, $five,
$five.$unit, $five.$two, $five.$three, $unit.$ten
];
}
my @order = (
make_range(qw{ I V X }),
make_range(qw{ X L C }),
make_range(qw{ C D M }),
make_range(qw{ M I)) ((I)) }),
make_range(qw{ ((I)) I))) (((I))) }),
make_range(qw{ (((I))) I)))) ((((I)))) }),
make_range(qw{ ((((I)))) I))))) (((((I))))) }),
make_range(qw{ (((((I))))) I)))))) ((((((I)))))) }),
make_range(qw{ ((((((I)))))) I))))))) (((((((I))))))) }),
make_range(qw{ (((((((I))))))) I)))))))) ((((((((I)))))))) }),
);
my %val;
for my $power (0..$#order) {
@val{@{$order[$power]}} = map {$_*10**$power} 0..9;
}
my $roman = '('
. join(")(", map { join("|",map { quotemeta } reverse sort(@$_)) }
reverse @order)
. '|)'
;
sub from_roman {
my $roman_val = shift;
my @numerals = $roman_val =~ /(?:$roman)/ix;
my $arabic_num = 0;
for my $numeral (@numerals) {
$arabic_num += $val{$numeral};
}
return $arabic_num;
}
sub to_roman {
my @digits = split '', shift;
my $power = 0;
my $roman_num = "";
for my $digit (reverse @digits) {
$roman_num = $order[$power++][$digit] . $roman_num;
}
return $roman_num;
}
More information about the Melbourne-pm
mailing list