練習問題への解答
ローマン数字を整数に変換する。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 件のコメント:
コメントを投稿