Summary
I'm getting ready to dive back into a "more proper" project, so I wanted to take a moment to get my TDD hat on before doing so. I decided to tackle this Roman Numeral Kata for practice. The purpose of the function is simply to take in a Long and output a string containing the equivalent Roman Numeral.
I'm confident that it's accurate out to 3999. Beyond that a standard keyboard doesn't quite have the proper characters, so 4000 comes out as MMMM and 14841 comes out as MMMMMMMMMMMMMMDCCCXLI. Unit Tests can be found in this Gist if you're interested.
I didn't plan on doing this recursively. It just kind of came out naturally after a few refactorings. I did at one point extract a few functions to reduce the redundancy in the code below, but it really felt like it obfuscated the algorithm and hurt the clarity.
Questions
Was the recursive solution a good way to go? I feel like it's clear, but I have this nagging feeling there might be a better algorithm. (One that may involve determining decimal places perhaps?)
Is it dry enough as it is, or should I have extracted a few functions like this.
Private Function UpFrom(ByVal number As Long, ByVal from As Long) As String UpFrom = ConvertToRomanNumeral(from) & ConvertToRomanNumeral(number - from) End FunctionShould I have raised an error after reaching 4000, instead of returning "numeral like" strings?
Did I do anything else stupid or dirty? Anything you'd do differently?
Numbers.bas
Public Function ConvertToRomanNumeral(ByVal number As Long) As String
Dim result As String
Select Case number
Case 1
result = "I"
Case 2 To 3
result = ConvertToRomanNumeral(number - 1) & ConvertToRomanNumeral(1)
Case 4
result = ConvertToRomanNumeral(1) & ConvertToRomanNumeral(5)
Case 5
result = "V"
Case 6 To 8
result = ConvertToRomanNumeral(5) & ConvertToRomanNumeral(number - 5)
Case 9
result = ConvertToRomanNumeral(1) & ConvertToRomanNumeral(10)
Case 10
result = "X"
Case 11 To 39
result = ConvertToRomanNumeral(10) & ConvertToRomanNumeral(number - 10)
Case 40
result = ConvertToRomanNumeral(10) & ConvertToRomanNumeral(50)
Case 41 To 49
result = ConvertToRomanNumeral(40) & ConvertToRomanNumeral(number - 40)
Case 50
result = "L"
Case 51 To 89
result = ConvertToRomanNumeral(50) & ConvertToRomanNumeral(number - 50)
Case 90
result = ConvertToRomanNumeral(10) & ConvertToRomanNumeral(100)
Case 91 To 99
result = ConvertToRomanNumeral(90) & ConvertToRomanNumeral(number - 90)
Case 100
result = "C"
Case 101 To 399
result = ConvertToRomanNumeral(100) & ConvertToRomanNumeral(number - 100)
Case 400
result = ConvertToRomanNumeral(100) & ConvertToRomanNumeral(500)
Case 401 To 499
result = ConvertToRomanNumeral(400) & ConvertToRomanNumeral(number - 400)
Case 500
result = "D"
Case 501 To 899
result = ConvertToRomanNumeral(500) & ConvertToRomanNumeral(number - 500)
Case 900
result = ConvertToRomanNumeral(100) & ConvertToRomanNumeral(1000)
Case 901 To 999
result = ConvertToRomanNumeral(900) & ConvertToRomanNumeral(number - 900)
Case 1000
result = "M"
Case Else
result = ConvertToRomanNumeral(1000) & ConvertToRomanNumeral(number - 1000)
End Select
ConvertToRomanNumeral = result
End Function