Working through some Katas, I think I've come up with the most needlessly recursive Roman Numerals Decoder:
def detokenise(numerals : String) : Seq[Int] = numerals.map(_ match {
case 'I' => 1
case 'V' => 5
case 'X' => 10
case 'L' => 50
case 'C' => 100
case 'D' => 500
case 'M' => 1000
case _ => throw new Error("")
})
def calculate(values : Seq[Int]) : Int =
if (values.size == 0)
0
else if (values.size == 1)
values.head
else {
val max = values.max
val pre = values.takeWhile(_ != max)
val post = values.dropWhile(_ != max).drop(1)
max - calculate(pre) + calculate(post)
}
def romanToInt(numerals : String) = calculate(detokenise(numerals))
val testPairs = Seq((1,"I"),
(2,"II"),
(4,"IV"),
(5,"V"),
(9,"IX"),
(10,"X"),
(900,"CM"),
(1000,"M"),
(3497,"MMMCDXCVII"))
testPairs.foreach(a => {
val actualResult = romanToInt(a._2)
println("%s: Expected: %s, Actual: %s".format(a._2, a._1, actualResult))
assert(a._1 == actualResult)
})
Turns out you can replace the calculate with
def calculate(values : Seq[Int]) : Int =
values.foldRight((0, 0))((currentValue : Int, previousAndTotal : (Int,Int)) =>
if (previousAndTotal._1 > currentValue)
(currentValue, previousAndTotal._2 - currentValue)
else
(currentValue, previousAndTotal._2 + currentValue)
)._2
Or even just implement it as a reverse for-loop. Oops...Well, wouldn't be too bad if the first solution wasn't based on the pseudocode I came up with during an interview earlier today. Not even trying to 'show off how I can do recursion' or anything, that first one is just the in-code version of how I did the calculations in my head.