You are here: Partenza > Dive Into Python > Test delle unità di codice > Postscritto | << >> | ||||
Dive Into PythonPython per programmatori esperti |
Un lettore intelligente dopo aver letto la sezione precedente ha subito fatto il passo successivo. Il più grosso grattacapo (e peggioratore delle prestazioni) nel programma com'è scritto al momento è l'espressione regolare, che è necessaria perché non abbiamo altro modo di decomporre un numero romano. Ma ci sono solo 5000 numeri romani: perché non ci costruiamo all'inizio una tavola di riferimento e non usiamo quella? Questa idea è ancora migliore una volta capito che è possibile rimuovere del tutto l'uso delle espressioni regolari. Una volta costruita la tavola di riferimento per convertire interi in numeri romani, è possibile costruire la tavola inversa per convertire i numeri romani in interi.
Ma la cosa migliore è che questo lettore aveva già un set completo di test delle unità di codice. Anche se ha cambiato metà del codice nel modulo roman.py, i test delle unità di codice rimanevano gli stessi, cosicché ha potuto verificare che il nuovo codice funzionasse altrettando bene dell'originale.
Se non lo avete ancora fatto, potete scaricare questo ed altri esempi usati in questo libro.
#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, "decimals 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()
E allora, quanto è più veloce?
.............
----------------------------------------------------------------------
Ran 13 tests in 0.791s
OK
Ricordate, le migliori prestazioni mai ottenute nella versione originale erano 13 test in 3.315 secondi. Ovviamente, questo non è un paragone onesto, perché questa versione richiederà più tempo per essere importata (dato che è allora che vengono riempite le tavole di riferimento). Ma dato che il modulo è importato una sola volta, questo tempo è trascurabile in confronto a tutta l'esecuzione del programma.
La morale della favola?
<< Rifattorizzazione |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
Sommario >> |