整数・ローマ数字変換(Python版)
http://d.hatena.ne.jp/fumokmm/20110822/1314013182
アラビア数字 <=> ローマ数字変換を行う関数、arabicToRoman および romanToArabic を実装せよ。
条件)
・ローマ数字の表記法についてはローマ数字 - Wikipediaを参考にすること。
・ローマ数字は半角英「I,V,X,L,C,D,M,i,v,x,l,c,d,m」のみ使用した文字列とし、それ以外はエラーとする。
・アラビア数字は整数 1 から 3999 のみ使用するものとし、それ以外はエラーとする。変換例)
11 <=> XI
12 <=> XII
14 <=> XIV
18 <=> XVIII
24 <=> XXIV
43 <=> XLIII
99 <=> XCIX
495 <=> CDXCV
1888 <=> MDCCCLXXXVIII
1945 <=> MCMXLV
3999 <=> MMMCMXCIXコードサンプル)
arabicToRoman(11) => "XI"
romanToArabic("MDCCCLXXXVIII") => 1888
romanToArabic("mdccclxxxviii") => 1888
romanToArabic("McmXLv") => 1945
arabicToRoman(0) => エラー
romanToArabic("A") => エラー
Pythonで解いてみました。関数名は変えてあります。
#!python3
#encoding:shift-jis
import re
DIGIT_STRINGS = {
1: dict(enumerate("I II III IV V VI VII VIII IX".split(), 1)),
2: dict(enumerate("X XX XXX XL L LX LXX LXXX XC".split(), 1)),
3: dict(enumerate("C CC CCC CD D DC DCC DCCC CM".split(), 1)),
4: dict(enumerate("M MM MMM".split(), 1)),
}
ROMAN_REGEX = re.compile(r"""
(?:(?P<_3000>MMM)|(?P<_2000>MM)|(?P<_1000>M))?
(?:(?P<_900>CM)|(?P<_800>DCCC)|(?P<_700>DCC)|(?P<_600>DC)
|(?P<_500>D)|(?P<_400>CD)|(?P<_300>CCC)|(?P<_200>CC)|(?P<_100>C))?
(?:(?P<_90>XC)|(?P<_80>LXXX)|(?P<_70>LXX)|(?P<_60>LX)
|(?P<_50>L)|(?P<_40>XL)|(?P<_30>XXX)|(?P<_20>XX)|(?P<_10>X))?
(?:(?P<_9>IX)|(?P<_8>VIII)|(?P<_7>VII)|(?P<_6>VI)
|(?P<_5>V)|(?P<_4>IV)|(?P<_3>III)|(?P<_2>II)|(?P<_1>I))?
$
""", re.VERBOSE | re.IGNORECASE)
def roman_to_int(roman):
"""
>>> roman_to_int("XI")
11
>>> roman_to_int("MDCCCLXXXVIII")
1888
>>> roman_to_int("MMMCMXCIX")
3999
>>> roman_to_int("McmXLv")
1945
>>> roman_to_int("CCCC")
Traceback (most recent call last):
ValueError: 'CCCC' is not valid roman
>>> roman_to_int("A")
Traceback (most recent call last):
ValueError: 'A' is not valid roman
>>> roman_to_int("")
Traceback (most recent call last):
ValueError: '' is not valid roman
"""
if not roman:
raise ValueError("{} is not valid roman".format(repr(roman)))
m = ROMAN_REGEX.match(roman)
if m is None:
raise ValueError("{} is not valid roman".format(repr(roman)))
ret = 0
for key, value in m.groupdict().items():
if value is not None:
ret += int(key[1:])
assert ret >= 0
return ret
def int_to_roman(n):
"""
>>> int_to_roman(11)
'XI'
>>> int_to_roman(3999)
'MMMCMXCIX'
>>> int_to_roman(1888)
'MDCCCLXXXVIII'
>>> int_to_roman(4000)
Traceback (most recent call last):
ValueError: 4000 is not in range(1, 4000)
>>> int_to_roman(0)
Traceback (most recent call last):
ValueError: 0 is not in range(1, 4000)
>>> int_to_roman(0.1)
Traceback (most recent call last):
TypeError: 0.1 is not integer
>>> int_to_roman("10")
Traceback (most recent call last):
TypeError: '10' is not integer
>>>
"""
if not isinstance(n, int):
raise TypeError("{} is not integer".format(repr(n)))
if n not in range(1, 4000):
raise ValueError("{} is not in range(1, 4000)".format(repr(n)))
strs =
for digit, x in enumerate(str(n)[::-1], 1):
x = int(x)
if x > 0:
strs.append(DIGIT_STRINGS[digit][x])
return "".join(reversed(strs))
def int_to_digits(n, base=10):
"""
>>> int_to_digits(0)
>>> int_to_digits(1)
[1]
>>> int_to_digits(12345)
[5, 4, 3, 2, 1]
>>> int_to_digits(0, base=2)
>>> int_to_digits(1, base=2)
[1]
>>> int_to_digits(2, base=2)
[0, 1]
>>> int_to_digits(0b1011, base=2)
[1, 1, 0, 1]
>>> int_to_digits(3**4, base=3)
[0, 0, 0, 0, 1]
>>> int_to_digits(-1)
Traceback (most recent call last):
ValueError: -1 is not >= 0
>>> int_to_digits(1.0)
Traceback (most recent call last):
TypeError: 1.0 is not integer
"""
if not isinstance(n, int):
raise TypeError("{} is not integer".format(repr(n)))
if not isinstance(base, int):
raise TypeError("base {} is not integer".format(repr(base)))
if n < 0:
raise ValueError("{} is not >= 0".format(repr(n)))
if base < 2:
raise ValueError("base {} is not >= 2".format(repr(base)))
ret =
ret_append = ret.append
while n:
ret_append(n % base)
n //= base
return ret
if __name__ == "__main__":
import doctest
doctest.testmod()
docstringに書いてあるように、エラー処理もしてあります。