罗马数字
【题目】古罗马的数字表示体系特点之一是繁难。具说是因为宗教的原因不能在数字中使用“零”的概念。这种体系如今仍有少量使用。比如书籍的目录页,时钟的刻度等。
千级以内的表示法是这样的。一些固定的字母代表固定的数字:
I(1)V(5)X(10)L(50)C(100)D(500)M(1000)
然后,用它们的重复表达更大的数。1,2,3,4… 表示为:
I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV …
规律是:每个字母最多能重复3次。那如何表达4,9 呢? 分别为 5-1, 10-1, 就是说: I X C 这3个字母可以放在比它大的单位的左边,意思是从大单位中减去这个值。
实战一下:
CMLXXXIV 表示:984
CCCXCVI 表示: 396
请编程,从一个罗马数字串转换为十进制数字的形式。
一眼看上去,就是把每个罗马数字符号转成对应的真值,加起来就差不多了。
麻烦的是:4,9,40,90 …这些另类,怎样准确识别出来并恰当处理。
先上haskell代码:
romanNum :: String -> Int
romanNum s = sum $ map f值 s ++ zipWith f补 s (tail s)
where
f值 'I' = 1
f值 'V' = 5
f值 'X' = 10
f值 'L' = 50
f值 'C' = 100
f值 'D' = 500
f值 'M' = 1000
f值 _ = error "bad roman number"
f补 'I' 'V' = -2
f补 'I' 'X' = -2
f补 'X' 'L' = -20
f补 'X' 'C' = -20
f补 'C' 'D' = -200
f补 'C' 'M' = -200
f补 _ _ = 0
main :: IO ()
main = do
print $ romanNum "CMLXXXIV"
print $ romanNum "CCCXCVI"
看起来好像出奇地简单。因为它用了枚举
法。
就是把所有的特殊情况都逐一列举出来,分别进行订正。
所有字母都按原值加起来。对于特殊的情况再把多加的给退回来即可。
其实,特殊的情况很少,只有: 4, 9, 40, 90, 400, 900
这么少的情况,没有必要找出一个普遍适用的“规律”来,那样代码反而难懂且容易出现漏洞。
枚举法可以说是程序设计中最为朴素、最为可靠的方法。只要能用它就应该优先选它。除非遇到了其它约束的限制。