アラビア数字とローマ数字の相互変換

お題:アラビア数字・ローマ数字変換 - No Programming, No Life にインスパイヤされて、アラビア数字とローマ数字の相互変換をしてみます。

アラビア数字からローマ数字の変換は、本来であれば再帰の問題として解くのが正しそうですが、数を 4000 未満ということにしてしまえば、再帰を使わないのもコードが単純なだけに分かりやすいかもしれません。

ローマ数字からアラビア数字の変換は正規表現の置換で行えます。 特に数を数えるのに gsub() 関数を使っています。 良く sub() や gsub() 関数の戻り値に誤解がありますが、sub() や gsub() 関数は置換した個数を返すため、これを利用して個数を数えています。

#! /usr/local/bin/nawk -f
# arabic2roman.awk
# アラビア数字をローマ数字に変換する
# usage: nawk -f arabic2roman.awk num

BEGIN {
    arabic_num = ARGV[1];

    print "Original Arabic Number:  " arabic_num;
    print "Converted Roman Number:  " arabic2roman(arabic_num);
    print "Converted Arabic Number: " roman2arabic(arabic2roman(arabic_num));
}

# arabic2roman():   アラビア数字をローマ数字に変換する
#   in:     アラビア数字 num
#   out:    ローマ数字 roman_num
function arabic2roman(num,
            i, roman_num) {

    # 4000 未満の整数のみを扱う
    if (num >= 4000) {
        print "Can not calculate." > "/dev/stderr";
    }

    # 4000 未満の場合だと再帰で記述するよりも分かりやすいのでは?
    if (int(num / 1000) >= 1) {
        for (i = 1; i <= int(num / 1000); i++) {
            roman_num = roman_num "M";
        }
    }
    num = num - 1000 * int(num / 1000);

    if (int(num / 100) >= 1) {
        for (i = 1; i <= int(num / 100); i++) {
            roman_num = roman_num "C";
        }

        sub(/CCCCCCCCC/, "CM", roman_num);
        sub(/CCCCC/,     "D",  roman_num);
        sub(/CCCC/,      "CD", roman_num);
    }
    num = num - 100 * int(num / 100);

    if (int(num / 10) >= 1) {
        for (i = 1; i <= int(num / 10); i++) {
            roman_num = roman_num "X";
        }

        sub(/XXXXXXXXX/, "XC", roman_num);
        sub(/XXXXX/,     "L",  roman_num);
        sub(/XXXX/,      "XL", roman_num);
    }
    num = num - 10 * int(num / 10);

    if (num >= 1) {
        for (i = 1; i <= num; i++) {
            roman_num = roman_num "I";
        }

        sub(/IIIIIIIII/, "IX", roman_num);
        sub(/IIIII/,     "V",  roman_num);
        sub(/IIII/,      "IV", roman_num);
    }
    num = num - 10 * int(num / 10);

    return roman_num;
}

# roman2arabic():   ローマ数字をアラビア数字に変換する
#   in:     ローマ数字 num
#   out:    アラビア数字
function roman2arabic(num,
            num1000, num100, num10, num1) {
    sub(/CM/, "CCCCCCCCC", num);
    sub(/CD/, "CCCC",      num);
    sub(/D/,  "CCCCC",     num);
    sub(/XC/, "XXXXXXXXX", num);
    sub(/XL/, "XXXX",      num);
    sub(/L/,  "XXXXX",     num);
    sub(/IX/, "IIIIIIIII", num);
    sub(/IV/, "IIII",      num);
    sub(/V/,  "IIIII",     num);

    num1000 = gsub(/M/, "", num);
    num100  = gsub(/C/, "", num);
    num10   = gsub(/X/, "", num);
    num1    = gsub(/I/, "", num);

    return num1000 * 1000 + num100 * 100 + num10 * 10 + num1;
}

実行してみます。

$ gawk -f arabic2roman.awk 11
Original Arabic Number:  11
Converted Roman Number:  XI
Converted Arabic Number: 11

$ gawk -f arabic2roman.awk 12
Original Arabic Number:  12
Converted Roman Number:  XII
Converted Arabic Number: 12

$ gawk -f arabic2roman.awk 14
Original Arabic Number:  14
Converted Roman Number:  XIV
Converted Arabic Number: 14

$ gawk -f arabic2roman.awk 18
Original Arabic Number:  18
Converted Roman Number:  XVIII
Converted Arabic Number: 18

$ gawk -f arabic2roman.awk 24
Original Arabic Number:  24
Converted Roman Number:  XXIV
Converted Arabic Number: 24

$ gawk -f arabic2roman.awk 43
Original Arabic Number:  43
Converted Roman Number:  XLIII
Converted Arabic Number: 43

$ gawk -f arabic2roman.awk 99
Original Arabic Number:  99
Converted Roman Number:  XCIX
Converted Arabic Number: 99

$ gawk -f arabic2roman.awk 495
Original Arabic Number:  495
Converted Roman Number:  CDXCV
Converted Arabic Number: 495

$ gawk -f arabic2roman.awk 1888
Original Arabic Number:  1888
Converted Roman Number:  MDCCCLXXXVIII
Converted Arabic Number: 1888

$ gawk -f arabic2roman.awk 1945
Original Arabic Number:  1945
Converted Roman Number:  MCMXLV
Converted Arabic Number: 1945

$ gawk -f arabic2roman.awk 3999
Original Arabic Number:  3999
Converted Roman Number:  MMMCMXCIX
Converted Arabic Number: 3999

tag_nawk.png tag_nawk.png tag_nawk.png tag_nawk.png