在前一章,我们的“研究”是通过马上查看代码,并努力以最快的速度进行理解。即然你已经有了一些Python的经验,我们将回过头来,看一看在编码之前所发生的步骤。
本章我们将编写一组实用函数,用于罗马数字的转换。你很有可能已经看过罗马数字,尽管不认识它们。你可能在老电影电视节目中的版权信息中看到过(“Copyright
MCMXLVI”而不是“Copyright 1946”),或在图书馆或大学的贡献人名围墙上看到过(“established
MDCCCLXXXVIII”而不是“established 1888”)。也可能你在参考书的提纲或目录中看到过。它是一种数字表示系统,这种表示真正地让我们回到古罗马时代(由此而得名)。
在罗马数字中,有七个字符,它们以各种各样的方法进行重复和组合,用来表示数字。
- I = 1
- V = 5
- X = 10
- L = 50
- C = 100
- D = 500
- M = 1000
构造罗马数字有一些通用的规则:
- 字符是一个加在一个后面。I 是 1,II 是 2,III
是 3。VI 是 6 (按字面意思,“5 和 1”),VII
是 7,VIII 是 8。
- 10 的倍数字符(I,X,C,和M)最多可以重复 3 次。到了第
4 次,你必须从接着的最大的 5 的倍数字符作减法。你不能用 IIII 表示 4;而应该用 IV
来表示(5 少 1)。40 被写作 XL(50
少 10),41 为 XLI,42 为 XLII,43
为 XLIII,44 为 XLIV(“ 50 少 10,然后
5 少 1)。
- 同样,对于 9,你必须从接着的最大的 10 的倍数字符作减法:8 是 VIII,但
9 是 IX (“10 少 1”),不是 VIIII
(因为 I 字符不能被重复 4 次)。90 是 XC,900 是
CM。
- 5 的倍数字符不能被重复,10 总是表示为 X,决不会为 VV。100
总是 C,决不会为 LL。
- 罗马数字总是从最高到最低,从左向右读,所以字符的顺序非常重要。DC 是 600;CD
是一个完全不同的数字(400,“500 少 100”)。 CI
是 101;IC 甚至不是一个有效的罗马数字(因为你不能从 100 直接减 1;应该写成
XCIX,“100 少 10 ,接着10 少 1”)。
这些规则可以得出许多有趣的观察结果:
- 仅有一种正确的方法将一个数字表示为罗马数字。
- 反过来也是对的:如果一个字符串是一个有效的罗马数字,它只表示一个数字(也就是,它只能以一种方式读出来)。
- 用罗马数字能够表达有限范围的数字,明确为 1 到 3999。(罗马人的确有几种方法表达更大的数字,例如在数字上加一道表示这个值应乘上
1000,但我们不打算这样处理。出于本章的目的,罗马数字是从 1 到 3999。)
- 在罗马数字中没有 0 的表示。(真令人吃惊,古罗马人不把 0 看成一个数字。数字是用来对你的东西进行数数用的;你怎么能数你没有的东西呢?)
- 用罗马数字没有方法表示负数。
- 用罗马数字没有方法表示小数或分数。
知道这些以后,我们希望写出一套什么样的函数进行罗马数字的转换呢?
roman.py 的要求
- toRoman 应该返回所有从 1 到 3999 的数字的罗马数字表示。
- 当所给的整数超出 1 到 3999 时,toRoman 应该失败。
- 当给出一个非整数时,toRoman 应该失败。
- fromRoman 应该接收一个有效的罗马数字,并返回它所表示的数字。
- 当给出一个无效的罗马数字时,fromRoman 应该失败。
- 如果你接收一个数字,将其转换成罗马数字,接着再转换回数字,最后应该是开始时的数字。所以对所有在 1 到 3999
中的 n ,fromRoman(toRoman(n)) == n 。
- toRoman 应该总是返回使用大写字母的罗马数字。
- fromRoman 应该只接收大写罗马数字(也就是当给出小写输入时,它应该出错)。
进一步阅读
- 这个站点
有更多的关于罗马数字的内容,它包括了罗马迷人的历史和其它文明是如何真正使用它们的(简单地说:偶然的和不一致的)。