Location : Home > Languages > Perl > Package Title : Math::Roman - Arbitrary sized Roman numbers and conversion from and to Arabic. |
![]() |
Math::Roman - ローマ数字とアラビア数字の変換
use Math::Roman qw(roman); $a = new Math::Roman 'MCMLXXIII'; # 1973 $b = roman('MCMLXI'); # 1961 print $a - $b,"\n"; # 'XII' $d = Math::Roman->bzero(); # '' $d++; # 'I' $d += 1998; # 'MCMXCIX' $d -= 'MCM'; # 'XCIX' print "$d\n"; # "MCMIC" print $d->as_number(),"\n"; # Math::BigInt "+1999"
perl5.005, Exporter, Math::BigInt
デフォルトでは何もエクスポートしないが、as_number(), roman(), error() はエクスポートすることができる。
どうも私はPerlのローマ数字ウィルス(Perligata-Virus)に感染したようだ。^^;
本モジュールではローマ数字をあたかも大整数のように計算することができる。数字は任意長でよく、Math::BigInt で使える関数は使うことができる。
単独のローマ数字は以下の通りである。
I 1 V 5 X 10 L 50 C 100 D 500 M 1000
以下の規則(きわめて現代的)が適用される。
I, X, C は3回まで繰り返して使うことができ、V, L, D は1度のみ使うことができる。
技術的には M は4回まで使うことができるが、本モジュールでは任意の大きさの数を扱うためこの制限がない。
ローマ数字はトークンから成り立っており、各トークンは IVXLCDM のうちの1文字または2文字から成り立っている。最初の文字は2番目の文字よりも小さい。後者の場合は最初の文字を2番めの文字が示す値から減じる。(たとえば IV は6ではなく4を表す。)
小さいほうの数字は10のべき乗(I, X, C)で、それ自身の値の10倍より大きくない値の前に来る。小さいほうの値は少なくともその値の10倍より大きい値の前に来て(たとえば LXC は妥当でない)、以下に続く値から1を減じた値よりも大きい値でなければならない(CMD は妥当でない)。
各トークンはその後のトークンより小さくなければならない(たとえば IIV は I が IV より小さいので妥当でない)。
入力は確認され、不適であれば 'NaN' を返す。次のローマ数字を生成するまでならその原因を Math::Roman::error() で取得することができる。
ローマ数字が構成可能な妥当なトークンのデフォルトのリストは以下の通りである。
III 3 XXX 30 CCC 300 II 2 XX 20 CC 200 IV 4 IX 9 XL 40 XC 90 CD 400 CM 900 I 1 V 5 X 10 L 50 C 100 D 500 M 1000
妥当でないトークンのデフォルトのリストは以下の通りである。
IIII XXXX CCCC DD LL VV C[MD][CDM] X[LC][XLCDM] I[VX][IVXLCDM]
詳しくは http://netdirect.net/~charta/Roman_numerals.html で確認すべし。
出力は常に可能な限り短い形式で行われ、トークンは降順に並び替えられる。
Math::Roman::tokens() を用いて定義されたトークンとその値を取得することができる。-1 とともに返されたトークンは妥当でなく、他は妥当である。フォーマットは token0, value0, token1, value1... という形式である。Math::Roman::tokens() で独自のものを設定し格納することができる。ルーチンは token, value, token, value... という形式の配列であることを想定している。各トークンは単一の文字列または正規表現である。値 -1 は妥当でないトークンであることを示す。
以下に他のルールはそのままで引き算を削除した(足し算だけが妥当)例を示す。このとき 'XIIII' は 14 となり、トークン集合は再定義され、'AAB' は 25 とパースされる。
use Math::Roman; Math::Roman::tokens( qw(I 1 V 5 X 10 L 50 C 100 D 500 M 1000)); $r = Math::Roman::roman('XIIII'); print "'$r' is ",$r->as_number(),"\n"; $r = Math::Roman::roman('XV'); print "'$r' is ",$r->as_number(),"\n"; Math::Roman::tokens ( qw(A 10 B 5) ); $r = Math::Roman::roman('AAB'); print "'$r' is ",$r->as_number(),"\n";
他のアイデアとしては、記号にダッシュをつけることである。これは1,000を乗じることを意味する。これは ASCII で表現することは困難であるので、小文字で表すこととすると以下のようになる。
use Math::Roman; # まちがって 'M' を定義しないでおくかも知れないが、あまりにも多くの 'M' はスクリーンに適合しない。 print 'old: ',new Math::Roman ('+12345678901234567890'),"\n"; @a = Math::Roman::tokens(); push @a, qw ( v 5000 x 10000 l 50000 c 100000 d 500000 m 1000000 ); Math::Roman::tokens(@a); print 'new: ',new Math::Roman ('+12345678901234567890'),"\n";
new()
new();
新しく Math::Roman オブジェクトを生成する。引数は、 /^[IVXLCDM]*$/ の形態の(さらなるルールは上を見よ) 'MCMLXXIII' (1973) のような文字列としてのローマ数字である。または Math::BigInt で用いられる文字列による数値である。
roman()
roman();
new と同じ動きをするが、短いコードでインポートできる。
error()
Math::Roman::error();
最近の数値生成で結果が NaN であったときのエラーを返す。
bstr()
$roman->bstr();
あらかじめ設定されたルールに従ったローマ数字の内部における値を表す文字列を返す。0は '' で表される。出力は立つなトークンからなり、符号を含まない。符号が知りたい場合には as_number() を用いる。
この関数は常に可能な限り最短の形態を生成する。
as_number()
$roman->as_number();
内部値を示す文字列をアラビア数字として符号をつけて返す。
計算を行うには、内部的に Math::BigInt を用いており、全てオーバーロードされる。ローマ数字には0も不の値もないが、本モジュールでは両方とも扱う。ローマ数字では絶対値だけが取得され、sign() または as_number() を用いたときのみ符号を得ることができる。
use Math::Roman qw(roman); print Math::Roman->new('MCMLXXII')->as_number(),"\n"; print Math::Roman->new('LXXXI')->as_number(),"\n"; print roman('MDCCCLXXXVIII')->as_number(),"\n"; $a = roman('1311'); print "$a is ",$a->as_number(),"\n"; $a = roman('MCMLXXII'); print "\$a is now $a (",$a->as_number(),")\n"; $a++; $a += 'MCMXII'; $a = $a * 'X' - 'I'; print "\$a is now $a (",$a->as_number(),")\n";
内部における数の長さ
実際の計算には Math::BigInt における制限と同じものが適用される。
出力の長さ
ローマ数字における出力は、最も大きな数字が 65536 回までに制限されている。これはデフォルトでは 'M' であるので出力可能な最大のローマ数字は 65536000 - であり、これは 64 KBytes の M が並ぶことになるが、誰が本当にこれが必要となるのか?
数の規則
「各トークンはその前のトークンよりも大きくなければならない」という規則は決定的で打ち勝つことができない。だから 'IIX' を12と再定義しない限り引き算のない数値での 'IIX' は妥当ではない。
関数のインポート
badd() のような通常の数学関数を以下のように書いてインポートすることはできない。
use Math::Roman qw(badd); # これは落ちる。 $a = badd('MCM','M'); # 機能しない。 $a = Math::Roman::badd('MCM','M'); # これも動かない。
これを機能するようにすることは可能であるが、きわめて大量のコピー&ペーストが必要となり、各計算において大きなオーバーヘッドとなる。実際にはあまり必要でないので以下のようにすればよい。
use Math::Roman; $a = new Math::Roman 'MCM'; $a += 'M'; # イケてない? $a = Math::Roman->badd('MCM','M'); # またはこれとか。
トークンとしての '0'-'9'
new() において 0-9 をトークンとして用いると、0-9 だけからなる引数を与えた際に誤った結果を生成する。これはまず BigInt が構成しようとするためである。
本プログラムはフリーソフトウェアであり、Perl 本体と同等の条件で修正/再配布してもよい。
本モジュールをユーザのプロジェクトに利用するなら連絡して欲しい。私のコードがどのように役立っているかを知りたいので。
Copyright (C) MCMXCIX-MMIV by Tels <http://bloodgate.com/>
![]() |
Updated : 2007/08/08 |