[QUIZ] Roman Numerals (#22)

J

Jason Bailey

Thank you :)

I like to see how small and concise I can make my code and with a new
language its quite a challenge.

As a side commentary I did get annoyed with ruby a couple of times. An
example being the zero based comparison check.

I kept going down paths such as

rom.index(key).zero?

Since that made a lot of sense, then realizing that it wouldn't work
because the index method could return a nil as well as a number.

I'm definitly looking forward to the next one now.

jason
 
Y

Yannick Turgeon

Here is my solution:

Being a 10-days-old Rubyist, I have to confest that I spent a lot of time
solving this problem and trying just for fun to make it more and more
concise at the price of clarity and no error handling. I've learned a lot
during the process. A very good problem to solve for beginners.

Yannick

--------------------
ARABIC = {"I" => 1, "IV" => 4, "V" => 5, "IX" => 9, "X" => 10, "XL" => 40,
"L" => 50, "XC" => 90, "C" => 100, "CD" => 400, "D" => 500, "CM" => 900,
"M" => 1000}
ROMAN = ARABIC.invert

def toInt(roman)
roman.reverse.split(//).inject(0) do |acc, letter|
acc += ARABIC[letter] * (acc > ARABIC[letter] * 3 ? -1 : 1)
end
end

def toRoman(number)
ARABIC.values.sort.reverse.inject("") do |acc, value|
mult, number = number.divmod(value)
acc << ROMAN[value] * mult
end
end

STDIN.readlines.each{|line| line.chomp!; print line, " = ", line.to_i > 0 ?
toRoman(line.to_i) : toInt(line), "\n"}
-------------------


Or Object Oriented:
-------------------
ARABIC = {"I" => 1, "IV" => 4, "V" => 5, "IX" => 9, "X" => 10, "XL" => 40,
"L" => 50, "XC" => 90, "C" => 100, "CD" => 400, "D" => 500, "CM" => 900,
"M" => 1000}
ROMAN = ARABIC.invert

class String
def roman_to_i()
self.reverse.split(//).inject(0) do |acc, letter|
acc += ARABIC[letter] * (acc > ARABIC[letter] * 3 ? -1 : 1)
end
end
end

class Integer
def to_roman()
remainder = self
ARABIC.values.sort.reverse.inject("") do |acc, value|
mult, remainder = remainder.divmod(value)
acc << ROMAN[value] * mult
end
end
end

STDIN.readlines.each{|line| line.chomp!; print line, " = ", line.to_i > 0 ?
line.to_i.to_roman : line.roman_to_i, "\n"}
 
A

aGorilla

Thx much, had a lot of fun doing this one, glad I had the time to play
with it.

ps: would've replied sooner, but seems the ml/usenet gateway is down
for a bit.
 
F

Fear Dubh

And my solution (not as nice-looking as Jason's):

class Fixnum
@@roman_chars = ['M', 'D', 'C', 'L', 'X', 'V', 'I']

def to_roman
n = self
if n > 3999 || n <= 0
return to_s
end
r = ''
i = 0
for v in [1000, 100, 10, 1]
if n >= v
m = n / v
r <<
if m == 9 then @@roman_chars + @@roman_chars[i - 2]
elsif m > 5 then @@roman_chars[i - 1] + @@roman_chars * (m -
5)
elsif m == 5 then @@roman_chars[i - 1]
elsif m == 4 then @@roman_chars + @@roman_chars[i - 1]
else @@roman_chars * m
end
n -= v * m
end
i += 2
end
return r
end
end

class String
@@roman_re = /^[IVXLCDM]+$/
@@roman_values = {'I' => 1, 'V' => 5, 'X' => 10, 'L' => 50,
'C' => 100, 'D' => 500, 'M' => 1000}

alias to_i_not_roman to_i

def to_i
s = self.upcase
if s !~ @@roman_re
return to_i_not_roman
end
n = 0
last_val = 0
(s.size - 1).downto 0 do |i|
val = @@roman_values[s[i, 1]]
if val >= last_val
n += val
else
n -= val
end
last_val = val
end
if n.to_roman != s
return to_i_not_roman
end
return n
end
end

for line in STDIN
line.chomp!
if line =~ /^\d+$/
print line.to_i.to_roman, "\n"
else
print line.to_i, "\n"
end
end
 
J

James Edward Gray II

Here's my own late entry for this party. My use of a prebuilt Array
means all the others are faster than mine, at least on small data sets.
;)

James Edward Gray II

#!/usr/local/bin/ruby -w

ROMAN_MAP = { 1 => "I",
4 => "IV",
5 => "V",
9 => "IX",
10 => "X",
40 => "XL",
50 => "L",
90 => "XC",
100 => "C",
400 => "CD",
500 => "D",
900 => "CM",
1000 => "M" }
ROMAN_NUMERALS = Array.new(3999) do |index|
target = index + 1
ROMAN_MAP.keys.sort { |a, b| b <=> a }.inject("") do |roman, div|
times, target = target.divmod(div)
roman << ROMAN_MAP[div] * times
end
end

IS_ROMAN = /^#{ ROMAN_MAP.keys.sort { |a, b| b <=> a }.inject("") do
|exp, n|
num = ROMAN_MAP[n]
exp << if num.length == 2 then "(?:#{num})?" else num + "{0,3}" end
end }$/
IS_ARABIC = /^(?:[123]\d{3}|[1-9]\d{0,2})$/

if __FILE__ == $0
ARGF.each_line() do |line|
line.chomp!
case line
when IS_ROMAN then puts ROMAN_NUMERALS.index(line) + 1
when IS_ARABIC then puts ROMAN_NUMERALS[line.to_i - 1]
else raise "Invalid input: #{line}"
end
end
end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top