15.4. 后记

聪明的读者在学习前一节时想得会更深入一层。现在写的这个程序中最令人头痛的性能负担是正则表达式,但它是必需的,因为没有其它方法来识别罗马数字。但是,它们只有 5000 个,为什么不一次性地构建一个查询表来读取?不必用正则表达式凸现了这个主意的好处。你建立了整数到罗马数字查询表的时候,罗马数字到整数的逆向查询表也构建了。

更大的好处在于,你已经拥有一整套完全的单元测试。你修改了多半的代码,但单元测试还是一样的,因此你可以确定你的新代码与来的代码一样可以正常工作。

例 15.17. roman9.py

这个文件可以在例子目录下的 py/roman/stage9/ 目录中找到。

如果您还没有下载本书附带的样例程序, 可以 下载本程序和其他样例程序

#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class InvalidRomanNumeralError(RomanError): pass

#Roman numerals must be less than 5000
MAX_ROMAN_NUMERAL = 4999

#Define digit mapping
romanNumeralMap = (('M',  1000),
                   ('CM', 900),
                   ('D',  500),
                   ('CD', 400),
                   ('C',  100),
                   ('XC', 90),
                   ('L',  50),
                   ('XL', 40),
                   ('X',  10),
                   ('IX', 9),
                   ('V',  5),
                   ('IV', 4),
                   ('I',  1))

#Create tables for fast conversion of roman numerals.
#See fillLookupTables() below.
toRomanTable = [ None ]  # Skip an index since Roman numerals have no zero
fromRomanTable = {}

def toRoman(n):
    """convert integer to Roman numeral"""
    if not (0 < n <= MAX_ROMAN_NUMERAL):
        raise OutOfRangeError, "number out of range (must be 1..%s)" % MAX_ROMAN_NUMERAL
    if int(n) <> n:
        raise NotIntegerError, "non-integers can not be converted"
    return toRomanTable[n]

def fromRoman(s):
    """convert Roman numeral to integer"""
    if not s:
        raise InvalidRomanNumeralError, "Input can not be blank"
    if not fromRomanTable.has_key(s):
        raise InvalidRomanNumeralError, "Invalid Roman numeral: %s" % s
    return fromRomanTable[s]

def toRomanDynamic(n):
    """convert integer to Roman numeral using dynamic programming"""
    result = ""
    for numeral, integer in romanNumeralMap:
        if n >= integer:
            result = numeral
            n -= integer
            break
    if n > 0:
        result += toRomanTable[n]
    return result

def fillLookupTables():
    """compute all the possible roman numerals"""
    #Save the values in two global tables to convert to and from integers.
    for integer in range(1, MAX_ROMAN_NUMERAL + 1):
        romanNumber = toRomanDynamic(integer)
        toRomanTable.append(romanNumber)
        fromRomanTable[romanNumber] = integer

fillLookupTables()

这样有多快呢?

例 15.18. 用 romantest9.py 测试 roman9.py 的结果


.............
----------------------------------------------------------------------
Ran 13 tests in 0.791s

OK

还记得吗?你原有版本的最快速度是 13 个测试耗时 3.315 秒。当然,这样的比较不完全公平,因为这个新版本需要更长的时间来导入 (当它填充查询表时)。但是导入只需一次,在运行过程中可以忽略。

这个重构的故事的寓意是什么?

重庆时时彩休市日期 静观其变不欲其乱 网赌时时彩输3200万 博众时时彩软件下载 时时彩掌赢专家真假
世爵平台登陆 天机时时彩软件源码 世爵时时彩平 助赢计划软件app 时时彩奇妙3.1破解版
时时彩彩虹计划官网 时时彩平台直招代理2 时时彩中奖规则及奖金 时时彩后三杀垃圾复式 时时彩手机软件吧
时时彩知乎 彩精灵时时彩软件v22 新疆时时彩奖金 腾龙时时彩软件电脑版 利用时时彩漏洞刷钱
上海快3开奖结果走势图图表 时时彩个位技巧 三公经费3000亿 幸运飞艇 北京快3开奖助手
多乐彩开奖 麻将牌全是坑 时时彩怎么才能稳赚 快三开奖结果查询今天计划♀排列3和值走势图计划♀青海11选5今日开奖结果计划♀快三走势图今天 广东十一选五官网
意大利赌场 网上赚钱是真的吗 河北省11选5走势图 北京赛车pk10最快直播 东方赛车一分彩网站
先知时时彩软件下载 上海十一选五历史开奖结果 幸运赛车投注 湖北11选5任五遗漏查询 2017海南环岛赛