clock tower
Многим в детстве нравились приключенческие игры с головоломками и загадками. Найти решение было личным вызовом, а его поиски напоминали охоту за сокровищами. И когда, наконец, загадка была разгадана, ощущения были потрясающими!
Регулярные выражения могут помочь ощутить тот же детский восторг. (Регулярные выражения на английском языке — regular expressions, сокращенно — regex, — прим. перев.). Их использование открывает множество возможностей. К тому же, когда читаешь регулярное выражение и понимаешь, что оно означает, — это как расшифровать египетский иероглиф, как внезапно обнаружить, что знаешь чужой язык!
На Codewars есть задачка: нужно написать функцию для конвертации римских чисел в арабские. Мы не будем разбирать решение этой задачи целиком. Вместо этого сосредоточимся на одном этапе: проверке, ввел ли пользователь валидное римское число.
Римские числа записываются римскими цифрами, которые вы видите в табличке:
| Тысячи | Сотни | Десятки | Единицы | |
|---|---|---|---|---|
| 1 | M | C | X | I |
| 2 | MM | CC | XX | II |
| 3 | MMM | CCC | XXX | III |
| 4 | CD | LX | IV | |
| 5 | D | L | V | |
| 6 | DC | LX | VI | |
| 7 | DCC | LXX | VII | |
| 8 | DCCC | LXXX | VIII | |
| 9 | CM | XC | IX |
Кажется, цифры для обозначения тысяч заканчиваются на [MMM], а значит, самое большое число, которое можно написать римскими цифрами, это 3999 ([MMMCMXCIX]). Для целей этой статьи и решения нашей задачи будем считать, что римские числа укладываются в диапазон от 1 до 3999. (На самом деле есть и большие числа, подробнее об этом можно почитать в Википедии, — прим. перев.).
Что нужно понимать, так это важность порядка в расположении римских цифр. Если вы поставите их не в том порядке, вы напишете неправильное или вообще нечитаемое число. Как было показано в таблице выше, число начинается с тысяч [M] 1000, затем идут сотни [D/C] 500/100, затем десятки [L/X] 50/10 и, наконец, единицы [V/I] 5/1.
Но это еще далеко не все! Цифры могут повторяться не больше трех раз подряд (например, 300 = CCC), а потом заменяются комбинацией двух цифр (400 = CD, а не CCCC). Дальше вы можете продолжать использовать C, но уже перед M, как в числе 900 (CM).
Не так-то все просто, верно?
Вы замечаете, как начинает вырисовываться регулярное выражение?
Мы будем использовать один очень полезный при написании регулярных выражений тег, — VERBOSE или re.X). Он позволяет разделять паттерн на несколько строк, чтобы сделать его более читаемым. Давайте попробуем!
import re
def is_roman_number(num):
pattern = re.compile(r"""
^M{0,3}
(CM|CD|D?C{0,3})?
(XC|XL|L?X{0,3})?
(IX|IV|V?I{0,3})?$
""", re.VERBOSE)
if re.match(pattern, num):
return True
return False Вах! Это уже потрясающе выглядит! Рассмотрим строки паттерна подробнее:
^M{0,3} = От 0 до 3 символов [M] в начале строки [^](CM|CD|D?C{0,3})? = Одна пара [CM] или одна пара [CD], или [D], за которой идет от 0 до 3 символов [C]. Каждый элемент является опциональным [?], так же, как и весь блок [()?](XC|XL|L?X{0,3})? = Одна пара [XC] или одна пара [XL] или [L], за которой идет от 0 до 3 символов [X]. Каждый элемент является опциональным [?], так же, как и весь блок [()?](IX|IV|V?I{0,3})?$ = Одна пара [IX] или одна пара [IV] или [V], за которой идет от 0 до 3 символов [I]. Каждый элемент является опциональным [?], так же, как и весь блок [()?], стоящий в конце строки [$]Используем fstring для вызова функции и сравнения строки с паттерном:
num_valid = 'MMDCCLXXIII'
num_invalid = 'CCCMMVIIVV'
print(f"{num_valid} - это {'не ' if not is_roman_number(num_valid) else ''}римское число")
print(f"{num_invalid} - это {'не ' if not is_roman_number(num_invalid) else ''}римское число")
# Вывод:
# MMDCCLXXIII - это римское число
# CCCMMVIIVV - это не римское число Не так уж и плохо! А теперь посмотрите на это и попробуйте сказать, что это не самая прекрасная вещь, которую вы видели в жизни!
^M{0,3}(CM|CD|D?C{0,3})?(XC|XL|L?X{0,3})?(IX|IV|V?I{0,3})?$' Если вы ищете работу на Python, на Talanto.work сейчас есть варианты для junior, middle, senior…
Рынок Python-вакансий большой, но удобным его назвать сложно. Формально вакансий много, однако в реальном поиске…
Срезы в Python — это больше, чем a[2:7]. В статье — быстрый разбор полезных приёмов:…
В этой статье мы рассмотрим возведение в степень в Python с помощью различных методов, от…
Дубликаты в списке Python — частая проблема. В статье показано, как быстро убрать повторы, в…
Услуга виртуального хостинга на SpaceWeb позволяет арендовать часть сервера (shared-сервера) и разместить на нём свой…