| 導航:起始頁 > Dive Into Python > 重構 > 后記 | << >> | ||||
深入 Python :Dive Into Python 中文版Python 從新手到專家 [Dip_5.4b_CPyUG_Release] |
|||||
聰明的讀者在學習前一節時想得會更深入一層。現在寫的這個程序中最令人頭痛的性能負擔是正則表達式,但它是必需的,因為沒有其它方法來識別羅馬數字。但是,它們只有 5000 個,為什么不一次性地構建一個查詢表來讀取?不必用正則表達式凸現了這個主意的好處。你建立了整數到羅馬數字查詢表的時候,羅馬數字到整數的逆向查詢表也構建了。
更大的好處在于,你已經擁有一整套完全的單元測試。你修改了多半的代碼,但單元測試還是一樣的,因此你可以確定你的新代碼與來的代碼一樣可以正常工作。
這個文件可以在例子目錄下的 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()
這樣有多快呢?
.............
----------------------------------------------------------------------
Ran 13 tests in 0.791s
OK
還記得嗎?你原有版本的最快速度是 13 個測試耗時 3.315 秒。當然,這樣的比較不完全公平,因為這個新版本需要更長的時間來導入 (當它填充查詢表時)。但是導入只需一次,在運行過程中可以忽略。
這個重構的故事的寓意是什么?
<< 重構 |
| 1 | 2 | 3 | 4 | 5 | |
小結 >> |