Maintenant que toRoman est terminé, nous
devons passer à fromRoman. Grâce à notre
structure de données élaborée qui fait correspondre les nombres
romains à des valeurs entières, ce n’est pas plus difficile que pour
toRoman.
Exemple 14.9. roman4.py
Ce fichier est disponible dans le sous-répertoire py/roman/stage4/ du répertoire des exemples.
"""Convert to and from Roman numerals"""#Define exceptionsclass RomanError(Exception): passclass OutOfRangeError(RomanError): passclass NotIntegerError(RomanError): passclass InvalidRomanNumeralError(RomanError): pass#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))
# toRoman function omitted for clarity (it hasn't changed)def fromRoman(s):
"""convert Roman numeral to integer"""
result = 0
index = 0
for numeral, integer in romanNumeralMap:
while s[index:index+len(numeral)] == numeral:
result += integer
index += len(numeral)
return result
Le principe est le même que pour toRoman.
Nous parcourons notre structure de données de chiffres romains (un
tuple de tuples) et au lieu de chercher la plus grande valeur
possible, nous cherchons la chaîne représentant les chiffres romains
les plus «haut» possible.
Exemple 14.10. Comment fromRoman fonctionne
Si vous n’êtes pas sûr de comprendre comment fonctionne
fromRoman, ajoutez une instruction print
à la fin de la boucle while :
while s[index:index+len(numeral)] == numeral:
result += integer
index += len(numeral)
print'found', numeral, 'of length', len(numeral), ', adding', integer
>>> import roman4>>> roman4.fromRoman('MCMLXXII')found M , of length 1, adding 1000
found CM , of length 2, adding 900
found L , of length 1, adding 50
found X , of length 1, adding 10
found X , of length 1, adding 10
found I , of length 1, adding 1
found I , of length 1, adding 1
1972
Exemple 14.11. Output of romantest4.py against roman4.py
fromRoman should only accept uppercase input ... FAIL
toRoman should always return uppercase ... ok
fromRoman should fail with malformed antecedents ... FAIL
fromRoman should fail with repeated pairs of numerals ... FAIL
fromRoman should fail with too many repeated numerals ... FAIL
fromRoman should give known result with known input ... ok
toRoman should give known result with known input ... ok
fromRoman(toRoman(n))==n for all n ... ok
toRoman should fail with non-integer input ... ok
toRoman should fail with negative input ... ok
toRoman should fail with large input ... ok
toRoman should fail with 0 input ... ok
Il y a deux bonnes nouvelles. La première est que
fromRoman marche pour des entrées correctes,
au moins pour toutes les valeurs
connues que nous testons.
La deuxième est que notre test de cohérence passe
également. Ces deux résultats nous permettent d’être
raisonnablement sûrs que toRoman comme
fromRoman marchent correctement pour toutes
les valeurs correctes. (Cela n’est pas garanti, il est
théoriquement possible que toRoman ait un
bogue qui produise des chiffres romains erronés pour certaines
entrées, et que
fromRoman ait un bogue correspondant
produisant les mêmes valeurs entières erronées pour les mêmes
chiffres romains. En fonction de votre application et de vos
besoins, cette possibilité peut vous déranger ; dans ce cas
écrivez des tests plus exhaustifs jusqu’à ce que vous ayez
l’esprit tranquille.)
======================================================================
FAIL: fromRoman should only accept uppercase input
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 156, in testFromRomanCase
roman4.fromRoman, numeral.lower())
File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with malformed antecedents
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 133, in testMalformedAntecedent
self.assertRaises(roman4.InvalidRomanNumeralError, roman4.fromRoman, s)
File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with repeated pairs of numerals
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 127, in testRepeatedPairs
self.assertRaises(roman4.InvalidRomanNumeralError, roman4.fromRoman, s)
File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
======================================================================
FAIL: fromRoman should fail with too many repeated numerals
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\docbook\dip\py\roman\stage4\romantest4.py", line 122, in testTooManyRepeatedNumerals
self.assertRaises(roman4.InvalidRomanNumeralError, roman4.fromRoman, s)
File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
raise self.failureException, excName
AssertionError: InvalidRomanNumeralError
----------------------------------------------------------------------
Ran 12 tests in 1.222s
FAILED (failures=4)