Ruby Example: toRoman

Problem: convert a number to its equivalent in Roman numerals.

Solution shows use of cases, array and string manipulation, exception raising, and an “each” block.

Working through the problem in irb, we start with an array of the Roman numerals:

irb(main):001:0> roman_array = ['i', 'v', 'x', 'l', 'c', 'd', 'm']
=> ["i", "v", "x", "l", "c", "d", "m"]

and a given number, num. For example, set

irb(main):002:0> num = 2683
=> 2683

Next, we turn this into an array of characters:

irb(main):003:0> char_array = num.to_s.split(//)
=> ["2", "6", "8", "3"]

Because we want to convert the ones column first, then tens etc. until we hit the end of the number, it’s convenient to reverse the array (instead of traversing it backwards):

irb(main):004:0> char_array.reverse!
=> ["3", "8", "6", "2"]

Here I’m going to throw in a method called trans that translates a single digit into roman numerals. I pass it a digit and an array with the Roman numerals associated with the correct power of ten (the one, five, and ten). For example, to translate the “3” in the number “37”, I call trans(3, ["x", "l", "c"]). Here’s the method:

irb(main):005:0> def trans(passed_num, arr)
irb(main):006:1> case passed_num
irb(main):007:2> when 1 then arr[0]
irb(main):008:2> when 2 then arr[0]*2
irb(main):009:2> when 3 then arr[0]*3
irb(main):010:2> when 4 then arr[0]+arr[1]
irb(main):011:2> when 5 then arr[1]
irb(main):012:2> when 6 then arr[1]+arr[0]
irb(main):013:2> when 7 then arr[1]+arr[0]*2
irb(main):014:2> when 8 then arr[1]+arr[0]*3
irb(main):015:2> when 9 then arr[0]+arr[2]
irb(main):016:2> else ''
irb(main):017:2> end
irb(main):018:1> end
=> nil

Okay, hoping that makes sense, lets get back to our char_array. For an element of char_array we can find the correct subarray of roman_array to send to trans because we know that the form of char_array is now [ones, tens, hundreds, thousands]. For the digit at char_array[0] we want to send the subarray roman_array[0,3]. For char_array[1] we want roman_array[2,3]. And so on. So for char_array[i], we want to pass the subarray roman_array[i*2, 3].The call, then, to change an element of char_array to its Roman counterpart, is

char_array[i] = trans(char_array[i].to_i, roman_array[i*2, 3])

(note: I’m overwriting the array instead of creating a new array with the Roman numerals, for no real reason at all).We want to do this for each element of char_array, which looks like this:

irb(main):019:0> char_array.each_index{|i| char_array[i] = trans(char_array[i].to_i, roman_array[i*2, 3])}
=> ["iii", "lxxx", "dc", "mm"]

We then want to reverse the order again, and stick the pieces back together:

>irb(main):020:0> char_array.reverse.to_s
=> "mmdclxxxiii"

which gives us our number, converted into Roman numerals.

Putting it all together (apologies for the lack of indentation):

def toRoman(num)
raise "this program only handles 1..3999" if num>=4000 or num<=0
roman_array = ['i', 'v', 'x', 'l', 'c', 'd', 'm']
char_array = num.to_s.split(//).reverse
char_array.each_index{|i| char_array[i] = trans(char_array[i].to_i, roman_array[i*2, 3])}.reverse.to_s
end
def trans(passed_num, arr)
case passed_num
when 1 then arr[0]
when 2 then arr[0]*2
when 3 then arr[0]*3
when 4 then arr[0]+arr[1]
when 5 then arr[1]
when 6 then arr[1]+arr[0]
when 7 then arr[1]+arr[0]*2
when 8 then arr[1]+arr[0]*3
when 9 then arr[0]+arr[2]
else ''
end
end

To use these methods as a stand-alone, useful .rb file, make the first line in the file:

#!usr/bin/ruby

or whatever works for your path, then at the end of the file, after the two methods:

if __FILE__ == $0
toRoman(ARGV[0].to_i)
end

This lets you call the method toRoman from the terminal, or use it as a library file in other code. A terminal call would look like:

$ ruby toRoman.rb 3195
mmmcxcv

while to use the method in other ruby code, include the line

irb(main):002:0> require 'toRoman.rb'
=> true

and then call the method as

irb(main):003:0> toRoman(3195)
=> "mmmcxcv"

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: