object RomanNumerals {
val ArabicNum = Vector(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
val RomanNum = Vector(
"M",
"CM",
"D",
"CD",
"C",
"XC",
"L",
"XL",
"X",
"IX",
"V",
"IV",
"I"
)
def roman(num: Int): String = {
Iterator
.iterate((num, 0, List[String]()))(tup =>
tup match {
case (num, idx, digits) if num >= ArabicNum(idx) =>
(num - ArabicNum(idx), idx, RomanNum(idx) :: digits)
case (num, idx, digits) =>
(num, idx + 1, digits)
}
)
.takeWhile(tup => tup._2 < 13)
.to(Seq)
.last match {
case (_, _, digits) => digits.reverse.mkString
}
}
}
This approach starts by defining two Vectors, one which contains the Arabic numbers and the other their corresponding Roman numerals.
The iterate()
method is initialized with the input Arabic number, the value 0
for the index, and a new
empty List
, all wrapped in a tuple.
The tuple is passed to the lambda which uses a match
to destructure the lambda.
The pattern matching is used to check if the number is greater than or equal to the Arabic number in the Vector
at the index.
If so, a new tuple is created with the number subtracted by the Arabic number, the index as is, and the List
prepended by the Roman numeral
in the Vector
at that index.
Otherwise, if the number is less than the the Arabic number at the index, then a new tuple is created with the number as is, the index added to by 1
,
and the List
as is.
The takewhile()
will keep calling the iterator while the index is less than 13
.
After all of the values in the Arabic Vector
have been checked against the number, the tuples are collected into a Seq
and the last
tuple is destructured by a match
that reverses the List
and converts it to a String
with mkString
, which is returned from the roman()
method.
Note that the operations on the values create new values instead of mutating them, thus supporting immutability.