Zanurkuj w Pythonie/roman.py, etap 4
Implementacja funkcji toRoman została zakończona, czas zająć się funkcją fromRoman. Dzięki bogatej strukturze danych przechowującej pewne wartości w reprezentacji rzymskiej wraz z ich wartościami liczbowymi, zadanie to nie będzie wcale trudniejsze, niż napisanie funkcji toRoman.
Przykład 14.9. roman4.py
Plik jest dostępny w katalogu in py/roman/stage4/ wewnątrz katalogu examples.
Jeśli jeszcze tego nie zrobiliście, możecie pobrać ten oraz inne przykłady używane w tej książce stąd.
"""Convert to and from Roman numerals"""
#Define exceptions
class RomanError(Exception): pass
class OutOfRangeError(RomanError): pass
class NotIntegerError(RomanError): pass
class 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: #(1)
result += integer
index += len(numeral)
return result
- Sposób działania jest taki sam jak w toRoman. Iterujemy po reprezentacjach rzymskich w strukturze danych (będącej krotką krotek), jednak zamiast dopasowywania największej wartości całkowitej tak często, jak to możliwe, dopasowujemy „najwyższą” reprezentację rzymską tak często, jak to możliwe.
Przykład 14.10. Jak działa fromRoman
Jeśli wciąż nie jesteście pewni, jak działa fromRoman, na końcu pętli while dodajcie instrukcję print:
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
Przykład 14.11. Wyjście programu romantest4.py testującego 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 #(1) toRoman should give known result with known input ... ok fromRoman(toRoman(n))==n for all n ... ok #(2) 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
- Mamy tu dwie interesujące wiadomości: po pierwsze, fromRoman działa dla poprawnych danych wejściowych, przynajmniej dla tych, które są zdefiniowane w teście poprawnych wartości.
- Po drugie, test zdroworozsądkowy również przeszedł. Wiedząc o tym, że przeszedł również test znanych wartości, możemy być raczej pewni, że zarówno fromRoman jak i toRoman działają poprawnie dla poprawnych danych wejściowych. (Nic nam tego jednak nie gwarantuje; teoretycznie jest możliwe, że w funkcji toRoman ukryty jest jakiś błąd, przez który dla pewnego zestawu danych wejściowych generowane są niepoprawne reprezentacje rzymskie, natomiast fromRoman zawierać może symetryczny błąd, który z kolei powoduje, że dla tych właśnie rzymskich reprezentacji generowane są niepoprawne wartości liczbowe. W zależności od zastosowań waszego kodu, a także wymagań, jakim ten kod podlega, może to stanowić dla was pewien problem; jeśli tak jest, dopiszcie więcej bardziej wszechstronnych testów tak, aby zmniejszyła się wasza niepewność.)
====================================================================== 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)