Introduction
A popular question on developer forums is how to create a Roman Numeral converter. It may seem to be a relatively easy task, but looks are deceiving, indeed. Today, I will show you a few different ways to create a decent Roman Numeral converter with Visual Basic.
First, Some History
A Roman Numeral is a number that is written in the way that the ancient Romans used to write numbers. This numeric system stayed the way of writing numbers in Europe until the Middle Ages. The Roman numerical system is composed of seven Latin letters which include: I (unus): one; V (quinque): five; X (decem): ten; L (quinquaginta): fifty; C (centum): one hundred; D (quingenti): five hundred; and M (mille): one thousand. By combining symbols, a Roman number is formed. There is no symbol for a zero, so there is also no need for zeros to keep place for other numbers. For example: A number such as 203 is written as CCIII.
If two letters are next to each other, each letter is worth powers of ten and the second letter is worth ten times the first. The ultimate value of this letter-group, or number, equals the second letter’s value subtracted by the first letter. This means then that IX equals nine, XC is ninety, and CM becomes nine hundred.
The Roman Numeral system is additive; this means that the numbers are simply added. For example, the number ‘IV’ means four. Talking about the Roman number four, this number is written like that for the simple reason that four or more symbols that look the same aren’t allowed to be written out. This means that you cannot have a number written as IIII—which ordinarily would mean four—but written as IV instead.
Enough history? Okay, let me move on. Let’s create a project that can convert Roman Numerals to ordinary numbers.
Create a new Visual Basic Windows Forms project and add one button to the form. Add a separate class. Feel free to use your own names, but remember that my names might be different than yours.
Let me pause here and explain the methodology first.
Man, I love patterns! If you look closely, you will see the same pattern being repeated depending on what the value of the number is. Before I get too far ahead of myself, look at the following:
1 I 2 II 3 III 4 IV 5 V 6 VI 7 VII 8 VIII 9 IX 10 X |
10 X 20 XX 30 XXX 40 XL 50 L 60 LX 70 LXX 80 LXXX 90 XC 100 C |
100 C 200 CC 300 CCC 400 CD 500 D 600 DC 700 DCC 800 DCCC 900 CM 1000 M |
Every sequence starts with a unique symbol as the first in the sequence, so that means 1 = I, 10 = X, 100 = C. The second in the sequence has two of the same symbol, and the third in the numeric sequence has three. Then, with the fourth item, it shows the first number in the current sequence, plus the next unique symbol. The fifth shows the unique symbol for 5, 50, 500, and so on. Six through eight follow the same sequence as one to three. At nine, there is again a change as well as 10.
Seeing this pattern allows you to better think of a solution. Always look long and hard at a situation; you will find answers…
Now, to put this newfound knowledge of this pattern as well as understanding that we only have seven unique symbols to work with, is quite easy, but it is a lot of work.
Let’s Add the First Class
This class is ultimately responsible for the input of ordinary numbers and the output of Roman numbers.
Public Class InputOutput Private intIn As Integer Private strOut As String Public Sub New(intInput As Integer) Me.intIn = intInput End Sub Public Property Input() As Integer Get Return intIn End Get Set(value As Integer) intIn = value End Set End Property Public Property Output() As String Get Return strOut End Get Set(value As String) strOut = value End Set End Property End Class Public MustInherit Class RomanExpression Public MustOverride Sub Interpret(value As InputOutput) End Class
Now, we need a class to format the output of Roman numbers so that it forms the correct ultimate number. Add this class now:
Public Class ParseDecimalToRoman Inherits RomanExpression Private AllExpressions As New List(Of RomanExpression)() _ From { _ New TenThousands(), _ New Thousands(), _ New Hundreds(), _ New Tens(), _ New Ones() _ } Public Overrides Sub Interpret(value As InputOutput) For Each reExp As RomanExpression In AllExpressions reExp.Interpret(value) Next End Sub End Class
Interpret outputs the information with the help of the InputOutput class. The List that was created contains classes that will make use of the same pattern I have mentioned earlier, as you will see now.
Add a class for multiples of One:
Class Ones Inherits Terminals Public Overrides Function OneStrings() As String Return "I" End Function Public Overrides Function FourStrings() As String Return "IV" End Function Public Overrides Function FiveStrings() As String Return "V" End Function Public Overrides Function NineStrings() As String Return "IX" End Function Public Overrides Function Multiply() As Integer Return 1 End Function End Class
This is very, extremely basic. Object-oriented programming 101. All the properties simply output the necessary string when a change should occur. I will explain the Terminals (which this class inherits from) class a bit later. Add the other classes:
Tens
Class Tens Inherits Terminals Public Overrides Function OneStrings() As String Return "X" End Function Public Overrides Function FourStrings() As String Return "XL" End Function Public Overrides Function FiveStrings() As String Return "L" End Function Public Overrides Function NineStrings() As String Return "XC" End Function Public Overrides Function Multiply() As Integer Return 10 End Function End Class
Hundreds
Class Hundreds Inherits Terminals Public Overrides Function OneStrings() As String Return "C" End Function Public Overrides Function FourStrings() As String Return "CD" End Function Public Overrides Function FiveStrings() As String Return "D" End Function Public Overrides Function NineStrings() As String Return "CM" End Function Public Overrides Function Multiply() As Integer Return 100 End Function End Class
Thousands
Class Thousands Inherits Terminals Public Overrides Function OneStrings() As String Return "M" End Function Public Overrides Function FourStrings() As String Return "MV" End Function Public Overrides Function FiveStrings() As String Return "V" End Function Public Overrides Function NineStrings() As String Return "MX" End Function Public Overrides Function Multiply() As Integer Return 1000 End Function End Class
Ten Thousands
Class TenThousands Inherits Terminals Public Overrides Function OneStrings() As String Return "X" End Function Public Overrides Function FourStrings() As String Return "XL" End Function Public Overrides Function FiveStrings() As String Return "L" End Function Public Overrides Function NineStrings() As String Return "XC" End Function Public Overrides Function Multiply() As Integer Return 10000 End Function End Class
I’ll stop here, but the same pattern applies to hundred thousands and millions.
Putting It All Together
Add the following class:
Public MustInherit Class Terminals Inherits RomanExpression Public Overrides Sub Interpret(value As InputOutput) While value.Input - 9 * Multiply() >= 0 value.Output += NineStrings() value.Input -= 9 * Multiply() End While While value.Input - 5 * Multiply() >= 0 value.Output += FiveStrings() value.Input -= 5 * Multiply() End While While value.Input - 4 * Multiply() >= 0 value.Output += FourStrings() value.Input -= 4 * Multiply() End While While value.Input - Multiply() >= 0 value.Output += OneStrings() value.Input -= Multiply() End While End Sub Public MustOverride Function OneStrings() As String Public MustOverride Function FourStrings() As String Public MustOverride Function FiveStrings() As String Public MustOverride Function NineStrings() As String Public MustOverride Function Multiply() As Integer End Class
This is where all the magic happens. It simply determines if the current value minus one of our pattern values multiplied by one, ten, hundred, thousand, and ten thousand. If it is, it will add the correct Roman number to the end of the string.
Add the following code behind the button you have added to Form 1 earlier:
Public Class Form1 Private Sub Button3_Click(sender As Object, e As EventArgs) _ Handles Button3.Click For i As Integer = 1 To 9999 Dim io As New InputOutput(i) Dim pdrParse = New ParseDecimalToRoman() pdrParse.Interpret(io) Console.WriteLine("{0}" & vbTab & "{1}", i, io.Output) Next End Sub End Class
I want to show the Roman Numbers up to 9999 into the Output window.
Conclusion
It is amazing how something this—should I say—trivial can end up having so much logic and work involved. Don’t you just love programming?