Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Приятная симметрия




Преобразование римского представления числа в десятичное выглядит более сложным, чем преобразование десятичной формы в римскую. Основная сложность заключается в валидации. Достаточно просто проверить, является ли целое число положительным; однако немного сложнее проверить, является ли строка корректным римским числом. К счастью, мы уже написали регулярное выражение, проверяющее римские числа.

Осталась задача преобразования самой строки. Как мы увидим через минуту, благодаря определенной нами структуре данных, ставящей в соответствие целым числам римские, реализация функции from_roman() является тривиальной задачей.

Но сначала тесты. Нам понадобятся известные значения для выборочной проверки правильности конвертирования. В качестве этих значений мы будем использовать описанный ранее набор known_values:

def test_from_roman_known_values(self):
'''from_roman should give known result with known input'''
for integer, numeral in self. known_values:
result = roman5. from_roman(numeral)
self. assertEqual(integer, result)

Здесь мы наблюдаем интересную симметрию. Функции to_roman() и from_roman() являются взаимообратными. Первая преобразует десятичное представление числа в римское, вторая же делает обратное преобразование. В теории мы должны иметь возможность " замкнуть круг", передав функции to_roman() число, затем передать результат выполнения функции from_roman(), возвращенное значение которой должно совпасть в исходным числом:

n = from_roman(to_roman(n)) for all values of n

В этом случае “all values” означает любое число в интервале [1, 3999]. Напишем тест, который передает все числа из этого интервала функции to_roman(), затем вызывает from_roman() и проверяет соответствие результата исходному числу:

class RoundtripCheck(unittest. TestCase):
def test_roundtrip(self):
'''from_roman(to_roman(n))==n for all n'''
for integer in range(1, 4000):
numeral = roman5. to_roman(integer)
result = roman5. from_roman(numeral)
self. assertEqual(integer, result)

Наши новые тесты пока не являются даже провальными - они завершились с ошибкой, так как мы еще не реализовали функцию from_roman():

you@localhost: ~/diveintopython3/examples$ python3 romantest5. py
E. E....
======================================================================
ERROR: test_from_roman_known_values (__main__. KnownValues)
from_roman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
File " romantest5. py", line 78, in test_from_roman_known_values
result = roman5. from_roman(numeral)
AttributeError: 'module' object has no attribute 'from_roman'
======================================================================
ERROR: test_roundtrip (__main__. RoundtripCheck)
from_roman(to_roman(n))==n for all n
----------------------------------------------------------------------
Traceback (most recent call last):
File " romantest5. py", line 103, in test_roundtrip
result = roman5. from_roman(numeral)
AttributeError: 'module' object has no attribute 'from_roman'
----------------------------------------------------------------------
Ran 7 tests in 0. 019s
FAILED (errors=2)

Создание заглушки функции решит эту проблему:

# roman5. py
def from_roman(s):
'''convert Roman numeral to integer'''

(Вы заметели? Я написал функцию, в которой нет ничего, кроме строки документации. Это нормально. Это Python. На самом деле, многие разработчики придерживаются именно такого стиля. “Не делай заглушек; документируй! ”)

Теперь тесты действительно являются провальными:

you@localhost: ~/diveintopython3/examples$ python3 romantest5. py
F. F....
======================================================================
FAIL: test_from_roman_known_values (__main__. KnownValues)
from_roman should give known result with known input
----------------------------------------------------------------------
Traceback (most recent call last):
File " romantest5. py", line 79, in test_from_roman_known_values
self. assertEqual(integer, result)
AssertionError: 1! = None
======================================================================
FAIL: test_roundtrip (__main__. RoundtripCheck)
from_roman(to_roman(n))==n for all n
----------------------------------------------------------------------
Traceback (most recent call last):
File " romantest5. py", line 104, in test_roundtrip
self. assertEqual(integer, result)
AssertionError: 1! = None
----------------------------------------------------------------------
Ran 7 tests in 0. 002s
FAILED (failures=2)

Теперь напишем функцию from_roman():

def from_roman(s):
" " " convert Roman numeral to integer" " "
result = 0
index = 0
for numeral, integer in roman_numeral_map:
while s[index: index+len(numeral)] == numeral: ①
result += integer
index += len(numeral)
return result

Стиль написания здесь точно такой же, как и в функции to_roman(). Мы пробегаем все значения roman_numeral_map, но вместо того, чтобы брать максимальное целое число, пока это возможно, мы берем максимальное римское представление числа и ищем его в строке, пока это возможно.

Если вам еще не совсем понятно, как работает функция from_roman(), добавьте вывод в конце цикла:

def from_roman(s):
" " " convert Roman numeral to integer" " "
result = 0
index = 0
for numeral, integer in roman_numeral_map:
while s[index: index+len(numeral)] == numeral:
result += integer
index += len(numeral)
print('found', numeral, 'of length', len(numeral), ', adding', integer)
> > > import roman5
> > > roman5. from_roman('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

Перезапустим тесты:

you@localhost: ~/diveintopython3/examples$ python3 romantest5. py
.......
----------------------------------------------------------------------
Ran 7 tests in 0. 060s
OK


У меня есть для вас две новости. Обе хорошие. Во-первых, функция from_roman() работает для правильного ввода (по крайней мере, для известных значений); во-вторых, наш " круг" замкнулся. Эти два факта позволяют вам быть уверенным в том, что функции to_roman() и from_roman() работают правильно для всех корректных значений. (На самом деле, правильность работы не гарантирована. Теоретически, функция to_roman() может иметь баг в реализации, из-за которого получается неправильное представление числа в римской форме для некоторых входных данных, а функция from_roman() может иметь " обратный" баг, из-за которого результатом выполнения является число, по счастливой случайности совпадающее с исходным. Если вас это беспокоит, напишите более сложные тесты. )

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...