Recurse Match

Roman Numerals
Roman Numerals in Python
ARABIC_NUM = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
ROMAN_NUM = ("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I")

def roman(number: int) -> str:
    return roman_recur(number, 0, [])

def roman_recur(num: int, idx: int, digits: list[str]):
    match (num, idx, digits):
        case [_, 13, digits]:
            return ''.join(digits[::-1])
        case [num, idx, digits] if num >= ARABIC_NUM[idx]:
            return roman_recur(num - ARABIC_NUM[idx], idx, [ROMAN_NUM[idx],] + digits)
        case [num, idx, digits]:
            return roman_recur(num, idx + 1, digits)

Recursion is possible in Python, but it is much less commonly used than in some other languages.

A limitation is the lack of tail-recursion optimization, which can easily trigger stack overflow if the recursion goes too deep. The maximum recursion depth for Python defaults to 1000 to avoid this overflow.

However, Roman numerals are so limited in scale that they could be an ideal use case for playing with recursion. In practice, there is no obvious advantage to recursion over using a loop (everything you can do with recursion you can do with a loop and vice-versa) .

Note the use of structural pattern matching, available in Python since version 3.10. There is also an official tutorial for this new feature.

The code above is adapted from a Scala approach, where it may be more appropriate.

Once we get past the unfamiliar-in-Python syntax, this code is doing essentially the same as other loop-over-romans approaches.

Without the pattern matching, a recursive approach might look something like this:

LOOKUP = [(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), (50, "L"),
    (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")]

def convert (number, idx, output):
    if idx > 12:
        return output
    val, ltr = LOOKUP[idx]
    if number >= val:
        return convert(number - val, idx, output + ltr)
    return convert(number, idx + 1, output)

def roman(number):
    return convert(number, 0, "")
8th May 2024 · Found it useful?

Other Approaches to Roman Numerals in Python

Other ways our community solved this exercise