I\'m trying to write a code that converts a user-inputted integer into its Roman numeral equivalent. What I have so far is:
The point of the generate_
Here's a lambda function for integer to roman numeral conversion, working up to 3999. It anchors some corner of the space of "unreadable things you probably don't actually want to do". But it may amuse someone:
lambda a: (
"".join(reversed([
"".join([
"IVXLCDM"[int(d)+i*2]
for d in [
"", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"][int(c)]])
for i,c in enumerate(reversed(str(a))) ]))
)
This approach gives an alternative to using arithmetical manipulations to isolate decimal digits and their place, as OP and many of the examples do. The approach here goes straight for converting the decimal number to a string. That way, digits can be isolated by list indexing. The data table is fairly compressed, and no subtraction or division is used.
Admittedly, in the form given, whatever is gained in brevity is immediately given up in readability. For people without time for puzzles, a version below is given that avoids list comprehension and lambda functions.
But I'll explain the lambda function version here...
Going from back to front:
Convert a decimal integer to a reversed string of its digits, and enumerate (i) over the reversed digits (c).
....
for i,c in enumerate(reversed(str(a)))
....
Convert each digit c back to an integer (range of 0-9), and use it as an index into a list of magic digit strings. The magic is explained a little later on.
....
[ "", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"][int(c)]])
....
Convert your selected magic digit string into a string of roman numeral
"digits". Basically, you now have your decimal digit expressed as roman
numeral digits appropriate to the original 10's place of the decimal digit.
This is the target of the generate_all_of_numeral
function used by the OP.
....
"".join([
"IVXLCDM"[int(d)+i*2]
for d in
....
Concatenate everything back in reversed order. The reversal is of the order of the digits, but order within the digits ("digits"?) is unaffected.
lambda a: (
"".join(reversed([
]))
Now, about that list of magic strings. It allows selecting the appropriate string of roman numeral digits (up to four of them, each being one of three types 0, 1, or 2) for each different 10's place that a decimal digit can occupy.
- 0 -> ""; roman numerals don't show zeros.
- 1 -> "0"; 0 + 2*i maps to I, X, C or M -> I, X, C or M.
- 2 -> "00"; like for 1, x2 -> II, XX, CC, MM.
- 3 -> "000"; like for 1, x3 -> III, XXX, CCC, MMM.
- 4 -> "01"; like for 1, then 1 +2*i maps to V, L, or D -> IV, XL, CD.
- 5 -> "1"; maps to odd roman numeral digits -> V, L, D.
- 6 -> "10"; reverse of 4 -> VI, LX, DC.
- 7 -> "100"; add another I/X/C -> VII LXX, DCC
- 8 -> "1000"; add another I/X/C -> VIII, LXXX, DCCC
- 9 -> "02"; like for 1, plus the next 10's level up (2 + i*2) -> IX, XC, CM.
At 4000 and above, this will throw an exception. "MMMM" = 4000, but this doesn't match the pattern anymore, breaking the assumptions of the algorithm.
...as promised above...
def int_to_roman(a):
all_roman_digits = []
digit_lookup_table = [
"", "0", "00", "000", "01",
"1", "10", "100", "1000", "02"]
for i,c in enumerate(reversed(str(a))):
roman_digit = ""
for d in digit_lookup_table[int(c)]:
roman_digit += ("IVXLCDM"[int(d)+i*2])
all_roman_digits.append(roman_digit)
return "".join(reversed(all_roman_digits))
I again left out exception trapping, but at least now there's a place to put it inline.