練習問題への解答
ローマン数字を整数に変換する。peekの実装で、''が読み出されたときにrewindしてしまっていた。
tes.py
import unittest
from roman import roman2numeric
# cases made from http://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%9E%E6%95%B0%E5%AD%97
class TestRome2NumericBasicCase(unittest.TestCase):
def test_none(self):
self.assertEqual(roman2numeric(''), 0)
def test_one(self):
self.assertEqual(roman2numeric('I'), 1)
def test_five(self):
self.assertEqual(roman2numeric('V'), 5)
def test_ten(self):
self.assertEqual(roman2numeric('X'), 10)
def test_fifty(self):
self.assertEqual(roman2numeric('L'), 50)
def test_hundred(self):
self.assertEqual(roman2numeric('C'), 100)
def test_fivehundred(self):
self.assertEqual(roman2numeric('D'), 500)
def test_thousand(self):
self.assertEqual(roman2numeric('M'), 1000)
class TestRome2NumericCombinedCase(unittest.TestCase):
def test_two(self):
self.assertEqual(roman2numeric('II'), 2)
def test_three(self):
self.assertEqual(roman2numeric('III'), 3)
def test_six(self):
self.assertEqual(roman2numeric('VI'), 6)
def test_seven(self):
self.assertEqual(roman2numeric('VII'), 7)
def test_eight(self):
self.assertEqual(roman2numeric('VIII'), 8)
def test_eleven(self):
self.assertEqual(roman2numeric('XI'), 11)
def test_twenty(self):
self.assertEqual(roman2numeric('XX'), 20)
def test_30(self):
self.assertEqual(roman2numeric('XXX'), 30)
def test_60(self):
self.assertEqual(roman2numeric('LX'), 60)
def test_70(self):
self.assertEqual(roman2numeric('LXX'), 70)
def test_80(self):
self.assertEqual(roman2numeric('LXXX'), 80)
def test_200(self):
self.assertEqual(roman2numeric('CC'), 200)
def test_300(self):
self.assertEqual(roman2numeric('CCC'), 300)
def test_600(self):
self.assertEqual(roman2numeric('DC'), 600)
def test_800(self):
self.assertEqual(roman2numeric('DCCC'), 800)
def test_1100(self):
self.assertEqual(roman2numeric('MC'), 1100)
def test_1200(self):
self.assertEqual(roman2numeric('MCC'), 1200)
def test_eighteeneityeight(self):
self.assertEqual(roman2numeric('MDCCCLXXXVIII'), 1888)
def test_twothousand(self):
self.assertEqual(roman2numeric('MM'), 2000)
class TestRome2NumericComplexCase(unittest.TestCase):
def test_four(self):
self.assertEqual(roman2numeric('IV'), 4)
def test_nine(self):
self.assertEqual(roman2numeric('IX'), 9)
def test_14(self):
self.assertEqual(roman2numeric('XIV'), 14)
def test_nineteen(self):
self.assertEqual(roman2numeric('XIX'), 19)
def test_fourty(self):
self.assertEqual(roman2numeric('XL'), 40)
def test_ninety(self):
self.assertEqual(roman2numeric('XC'), 90)
def test_ninetythree(self):
self.assertEqual(roman2numeric('XCIII'), 93)
def test_ninenine(self):
self.assertEqual(roman2numeric('XCIX'), 99)
def test_400(self):
self.assertEqual(roman2numeric('CD'), 400)
def test_900(self):
self.assertEqual(roman2numeric('CM'), 900)
def test_nineninenine(self):
self.assertEqual(roman2numeric('CMXCIX'), 999)
#self.assertRaise(roman2numeric('IM'), 999) #???
def test_nineteenninetynine(self):
self.assertEqual(roman2numeric('MCMXCIX'), 1999)
roman.py
import StringIO
def nullimple(s):
return 0
'''
cfg
R -> Letter*
Letter -> 'I' | 'V' | 'X' | 'L' | 'C' | 'D' | 'M'
# too large
Basic/Combined Level
'M'*'D'?'C'{0,3}'L'?'X'{0,3}'V'?'I'{0,3}
'''
def CombinedLevel(s):
r = 0
for d in s:
if d == 'M':
r += 1000
elif d == 'D':
r += 500
elif d == 'C':
r += 100
elif d == 'L':
r += 50
elif d == 'X':
r += 10
elif d == 'V':
r += 5
elif d == 'I':
r += 1
else:
raise 'error'
return r
def ComplexLevel(s):
'''
cfg
R -> thousands hundreds tens ones
thousands -> 'M' thousands | empty
hundreds -> 'C' 'M' | range500to800 | 'C''D' | range000to300
range500to800 -> 'D' range000to300
range000to300 -> 'C'{0, 3}
tens -> 'X' 'C' | range50to80 | 'X' 'L' | range00to30
range50to80 -> 'L' range00to30
range00to30 -> 'X'{0,3}
ones -> 'I''X' | range5to8 | 'I''V' | range0to3
range5to8 -> 'V' range0to3
range0to3 -> 'I'{0,3}
'''
global lookahead, result #ugh!
result = 0
buf = StringIO.StringIO(s)
lookahead = None
def roman():
''' R -> thousands hundreds tens ones '''
global lookahead
lookahead = buf.read(1)
while True:
if lookahead == 'M':
thousands()
else:
break
if lookahead in ('C', 'D'):
hundreds()
if lookahead in ('X', 'L'):
tens()
if lookahead in ('V', 'I'):
ones()
def thousands():
global result
match('M')
result += 1000
def hundreds():
''' hundreds -> 'C' 'M' | range500to800 | 'C''D' | range000to300 '''
global result
p = peek()
if p == 'M':
match('C')
match('M')
result += 900
return
elif p == 'D':
match('C')
match('D')
result += 400
return
elif lookahead == 'D':
range500to800()
else:
range000to300()
return
def range500to800():
global result
match('D')
result += 500
range000to300()
def range000to300():
global result
while True:
if lookahead == 'C':
match('C')
result += 100
else:
break
def tens():
''' tens -> 'X' 'C' | range50to80 | 'X' 'L' | range00to30'''
global result
p = peek()
if p == 'C':
match('X')
match('C')
result += 90
return
elif p == 'L':
match('X')
match('L')
result += 40
return
elif lookahead == 'L':
range50to80()
else:
range00to30()
def range50to80():
global result
match('L')
result += 50
range00to30()
def range00to30():
global result
while True:
if lookahead == 'X':
match('X')
result += 10
else:
break
def ones():
''' ones -> 'I''X' | range5to8 | 'I''V' | range0to3 '''
global result
p = peek()
if p == 'X':
match('I')
match('X')
result += 9
return
elif p == 'V':
match('I')
match('V')
result += 4
return
elif lookahead == 'V':
range5to8()
else:
range0to3()
def range5to8():
''' range5to8 -> 'V' range0to3 '''
global result
match('V')
result += 5
range0to3()
def range0to3():
''' range0to3 -> 'I'{0,3} '''
global result
while True:
if lookahead == 'I':
match('I')
result += 1
else:
break
def match(t):
global lookahead
if lookahead == t:
lookahead = buf.read(1)
else:
error('expected %s, but got %s'%(t, lookahead))
def peek():
#print 'peek pos%d %s'%(buf.tell(), buf.getvalue())
r = buf.read(1)
if r:
buf.seek(-1, 1)
#print 'peek pos%d %s'%(buf.tell(), buf.getvalue())
#print 'read:', repr(r)
return r
def error(msg):
raise Exception(msg)
roman()
return result
def roman2numeric(s):
#return nullimple(s)
#return CombinedLevel(s)
return ComplexLevel(s)
0 コメント:
コメントを投稿