Description
Today, most people in the world use Arabic numerals (0–9).
But if you travelled back two thousand years, you’d find that most Europeans were using Roman numerals instead.
To write a Roman numeral we use the following Latin letters, each of which has a value:
A Roman numeral is a sequence of these letters, and its value is the sum of the letters’ values.
For example, XVIII has the value 18 (10 + 5 + 1 + 1 + 1 = 18).
There’s one rule that makes things trickier though, and that’s that the same letter cannot be used more than three times in succession.
That means that we can’t express numbers such as 4 with the seemingly natural IIII.
Instead, for those numbers, we use a subtraction method between two letters.
So we think of 4 not as 1 + 1 + 1 + 1 but instead as 5 - 1.
And slightly confusingly to our modern thinking, we write the smaller number first.
This applies only in the following cases: 4 (IV), 9 (IX), 40 (XL), 90 (XC), 400 (CD) and 900 (CM).
Order matters in Roman numerals!
Letters (and the special compounds above) must be ordered by decreasing value from left to right.
Here are some examples:
105 => CV
---- => --
100 => C
+ 5 => V
106 => CVI
---- => --
100 => C
+ 5 => V
+ 1 => I
104 => CIV
---- => ---
100 => C
+ 4 => IV
And a final more complex example:
1996 => MCMXCVI
----- => -------
1000 => M
+ 900 => CM
+ 90 => XC
+ 5 => V
+ 1 => I
Instructions
Introduction
Your task is to convert a number from Arabic numerals to Roman numerals.
For this exercise, we are only concerned about traditional Roman numerals, in which the largest number is MMMCMXCIX (or 3,999).
There are lots of different ways to convert between Arabic and Roman numerals.
We recommend taking a naive approach first to familiarise yourself with the concept of Roman numerals and then search for more efficient methods.
Make sure to check out our Deep Dive video at the end to explore the different approaches you can take!
Dig Deeper
fold with repeat
fold with repeat
object RomanNumerals {
fun value(n: Int) =
listOf(
1000 to "M",
900 to "CM",
500 to "D",
400 to "CD",
100 to "C",
90 to "XC",
50 to "L",
40 to "XL",
10 to "X",
9 to "IX",
5 to "V",
4 to "IV",
1 to "I"
)
.fold(Pair(StringBuilder(), n)) { (output, runnyNum), (value, numeral) ->
when {
runnyNum >= value ->
output.append(numeral.repeat(runnyNum / value)) to
runnyNum % value
else -> output to runnyNum
}
}
.first
.toString()
}
An object declaration is used to define RomanNumerals as essentially a singleton object instantiation of the class.
This is sufficient, since there is no object state that needs to change with each call of the value method.
The listOf method is used to create a list of Pairs that associate an Arabic numeral with its Roman numeral.
The fold method is called on the List.
Its accumulating value is initialized with a Pair, with a StringBuilder for the output and an Int to hold the value of the input number
as it is calculated down.
The lambda of fold takes the accumulating value Pair as well as the Pair of each List element being iterated.
The running number is tested in a when expression.
If it is greater than or equal to the Arabic value in the currently iterated Pair,
then the repeat method is used to append the Roman numeral to the StringBuilder
for as many times as the running number can be divided by the Arabic value with a result greater than 0.
That arm of the when uses the to keyword to return the StringBuilder and the remainder of dividing the running number by the Arabic value.
If the running number is less than the Arabic value, then the when expression returns the StringBuilder and the running number as is.
When fold has iterated through all of the List elements, the first item in the accumulating value Pair is the StringBuilder.
The value function returns the result of calling the toString method on the StringBuilder.
Although the value function has multiple lines, it consists only of the one expression of chained methods, so it is defined using
single-expression function syntax, with the curly braces omitted and the return type inferred.
Source: Exercism kotlin/roman-numerals