This issue is a discussion for contributors to collaborate in getting ready to be featured in 48in24. Please refer to this forum topic for more info.
We will be featuring Roman Numerals from Feb 06 onwards.
Staff jobs
These are things for Erik/Jeremy to do:
Check/update exercise in Problem Specifications
Create + schedule video
Community jobs
For each track:
Implement Roman Numerals
Add approaches (and an approaches introduction!) for each idiomatic or interesting/educational approach.
Add video walkthroughs (record yourself solving and digging deeper into the exercise).
Highlight up to 16 different featured exercises (coming soon)
Existing Approaches
You can use these as the basis for approaches on your own tracks. Feel free to copy/paste/reuse/rewrite/etc as you see fit! Maybe ask ChatGPT to translate to your programming language.
I belatedly checked, and this is also available in Python.
It needs installation of a separate module, so I’m not yet sure if it’s available in the test runner (I’ll find out). If not, that’s probably a good thing!
For fans of obfuscated code, I bring you a couple of Python examples from past submissions. They pass all tests (except good taste?)
From youlifil:
def roman(number):
return ''.join(one*digit if digit<4 else one+five if digit==4 else five+one*(digit-5) if digit<9 else one+ten
for digit, (one,five,ten)
in zip([int(d) for d in str(number)], ["--MDCLXVI"[-i*2-1:-i*2-4:-1] for i in range(len(str(number))-1,-1,-1)]))
From MAPKarrenbelt:
from itertools import starmap
def roman(number: int) -> str:
orders = [(1000, "M "), (100, "CDM"), (10, "XLC"), (1, "IVX")]
options = lambda I, V, X: ["", I, I * 2, I * 3, I + V, V, V + I, V + I * 2, V + I * 3, I + X]
compute = lambda n, chars: options(*chars)[number % (n * 10) // n]
return "".join(starmap(compute, orders))
As you would guess, PEP-8 linters complain about both.
The latter looks a bit like how I solved it. But I think the linter might be fine with mine.
# The 10's, 5's and 1's position chars for 1, 10, 100, 1000.
DIGIT_CHARS = ["XVI", "CLX", "MDC", "??M"]
def roman(number: int) -> str:
"""Return the Roman numeral for a number."""
# Generate a mapping from numeric value to Roman numeral.
mapping = []
for position in range(len(DIGIT_CHARS) - 1, -1, -1):
# Values: 1000, 100, 10, 1
scale = 10 ** position
chars = DIGIT_CHARS[position]
# This might be: (9, IX) or (90, XC)
mapping.append((9 * scale, chars[2] + chars[0]))
# This might be: (5, V) or (50, D)
mapping.append((5 * scale, chars[1]))
# This might be: (4, IV) or (40, XD)
mapping.append((4 * scale, chars[2] + chars[1]))
mapping.append((1 * scale, chars[2]))
out = ""
for num, numerals in mapping:
while number >= num:
out += numerals
number -= num
return out
``
I don’t know if this is considered an unusual and/or interesting approach, but here it is:
def roman(number):
if number >= 900 and number < 1000 or number >= 400 and number < 500:
return 'C' + roman(number + 100)
elif number >= 90 and number < 100 or number >= 40 and number < 50:
return 'X' + roman(number + 10)
elif number == 9 or number == 4:
return 'I' + roman(number + 1)
elif number >= 1000:
return 'M' + roman(number - 1000)
elif number >= 500:
return 'D' + roman(number - 500)
elif number >= 100:
return 'C' + roman(number - 100)
elif number >= 50:
return 'L' + roman(number - 50)
elif number >= 10:
return 'X' + roman(number - 10)
elif number >= 5:
return 'V' + roman(number - 5)
elif number >= 1:
return 'I' + roman(number - 1)
else:
return ''
If the conversion is about to use a subractive form (for example, IX for 9), the above program appends the subracted numeral (I) to the output string, and then goes on converting the number incremented by the subracted amount (+ 1).
This results in a recursive process which oscillates between larger and smaller numbers to convert.
I’m still looking for some nice, weird or just generally interesting solutions. Bonus points if they’re not written in Python (we like to feature different languages).