Visit the roman-numerals exercise on Exercism to read the full instructions and download the exercise files.
Dig Deeper
Loop Over Romans
Loop Over Digits
roman <- function(arabic) {
conversion <- list(M = 1000, CM = 900, D = 500, CD = 400,
C = 100, XC = 90, L = 50, XL = 40,
X = 10, IX = 9, V = 5, IV = 4, I = 1)
result <- c()
for (roman in names(conversion)) {
num <- conversion[[roman]]
while (num <= arabic) {
result <- append(result, roman)
arabic <- arabic - num
}
}
paste(result, collapse = "")
}
This approach is one of a family, using some mapping from Arabic (decimal) to Roman numbers.
The code above uses a list with names. Using a pair of vectors is also possible, with a shared index from the seq_along().
roman <- function(arabic) {
nums <- c(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
romans <- c("M", "CM", "D", "CD", "C", "XC",
"L", "XL", "X", "IX", "V", "IV", "I")
result <- c()
for (i in seq_along(nums)) {
num <- nums[i]
while (num <= arabic) {
result <- append(result, romans[i])
arabic <- arabic - num
}
}
paste(result, collapse = "")
}
These solutions, and several others with different data structures for the lookup, share some common features:
- Some sort of translation lookup.
- Nested loops, a
whileand a for.
- At each step, find the largest number that can be subtracted from the decimal input and appended to the Roman representation.
Table Lookup
Table Lookup
roman <- function(arabic) {
conversion = list(
c("I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"),
c("X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"),
c("C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"),
c("M", "MM", "MMM"))
# convert integer to a vector of single digits
digits <- arabic |>
as.character() |>
strsplit("") |>
unlist() |>
as.numeric()
inverter <- length(digits) + 1
roman_digits <- c()
# iterate through the digits, converting to Roman at each step
for (i in seq_along(digits)) {
roman_digits <- c(roman_digits, conversion[[inverter - i]][digits[i]])
}
# convert to a single string and return
paste(roman_digits, collapse = "")
}
In this approach we loop over decimal digits, not their Roman equivalents.
The key point is to have a 2-dimensional lookup table, with each row corresponding to a separate digit: ones, tens, hundreds, thousands.
Each digit can then be converted to its Roman equivalent with a single lookup.
This relies on the fact that both systems are base-10, despite this being less obvious in the Roman case.
In the code above, the “table” is implemented as a list of vectors, which allows for different “row” lengths.
Other data structures are possible, such as dataframes or tibbles, provided the thousands row is padded with empty strings or NA values to make the table a regular rectangle.
Optional modifications
In the example code, we used the inverter variable to work bottom-to-top through the lookup table.
This allows working left-to-right through the decimal digits.
Alternatively, we could reverse the digits vector, go top-to-bottom through the lookup table, then reverse the roman_digits vector before the final paste().
On balance, this seems unnecessary.
Credit
This approach was adapted from one created by @cmcaine on the Julia track.
Source: Exercism r/roman-numerals