TDD Roman Numeral tutorial in Ruby

P

Phlip

I would change the entire approach to the following:

type Roman_Numerals is (I, II, III, IV, V, VI, VII, VIII);

Your implementation duplicates I, V, etc. If you were to resolve that
duplication, it would form little tables. Then adding L, C, M etc.
would get easier.

I ran the experiment in Ruby, and put the result here:

http://www.xpsd.org/cgi-bin/wiki?TestDrivenDevelopmentTutorialRomanNumerals

The (unfinished) implementation is here:

Symbols = {
1=>'I', 5=>'V', 10=>'X', 50=>'L', 100=>'C',
}

def roman(num)
return Symbols[num] if Symbols.has_key?(num)

[ [100, 1],
[100, 10],
[ 50, 10],
[ 10, 1],
[ 5, 1],
[ 1, 0],
].each do |cutPoint, subtractor|
if num > cutPoint then
return roman(cutPoint) + roman(num - cutPoint)
end

if num >= cutPoint - subtractor and num < cutPoint then
return roman(subtractor) + roman(num + subtractor)
end
end
end

That code can't do 49 (IL) yet, and it obviously can't do D or M yet.
But it's just as obvious how to add those.

That's the point: Removing duplication while growing a design in ways
that improve its extensibility. There's plenty of "duplication" of
various sorts in the above code. But merging it wouldn't make the
design more extensible in the direction of more advanced roman numeral
codes. It turned out that merging the components of the output strings
- I, V etc. - focused the design directly on what was important.
 
J

Josef 'Jupp' SCHUGT

Hi!

Wrote a simple conversion tool. Takes one command line argument.
Interpretes it as a decimal number. Prints roman number for it.
Returns 1 on error, 0 on success. Conversion of single digits has two
cases

a) digit is 4 or 9. In this case append n1 followed by n5 if
digit is 4 and by n1[i+1] if digit is 9.

b) digit is neither 4 nor 9. In this case append n5 if digit is
greater or equals 5. Then append d modulo 5 times n1.

As far as tables are concerned uses bare necessities - symbols need
to be stored in any case. Converting tool to a function is left as an
exercise :->

The algorithm is not new. It is a formal representation of what one
does when manually writing roman numbers. It can easily be expanded
to additional symbols by simply adding them to the lists.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#!/usr/bin/env ruby

n1 = [ 'I', 'X', 'C', 'M' ]
n5 = [ 'V', 'L', 'D', nil ]

max = 0

n1.each_index { |i| max += 3 * 10 ** i }
n5.each_index { |i| max += 5 * 10 ** i unless i.nil? }

exit 1 if ARGV.length != 1

x = ARGV.first.to_i
r = ""

exit 1 if x > max or x < 1

(n5.length - 1).downto(0) { |i|
d = x / 10 ** i
x -= d * 10 ** i
if d == 4 or d == 9
r << n1 + (d == 4 ? n5 : n1[i+1])
else
if d >= 5
r << n5
d -= 5
end
r << n1 * (d % 5)
end
}

puts r
exit 0

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Didn't find a simpler solution. Improvements are welcome.

Josef 'Jupp' SCHUGT
 
J

Josef 'Jupp' SCHUGT

Hi!

* David A. Black:
n5.each_index { |i| max += 5 * 10 ** i unless i.nil? }

i will never be nil -- it will always be an integer. (Did you mean
"unless n5.nil?'?)


You are right. Stupid mistake.

Josef 'Jupp' SCHUGT
 
K

Karsten Meier

Hello Romans :)

I have done a solution to work with romans for the pleac cookbook
project, see:

http://pleac.sourceforge.net/pleac_ruby/numbers.html
section "Working with Roman Numerals"

It does also the opposite conversation. I have looked at other solutions
in other languages before writing this, and was quite happy to came up
with this algorithm, because it is so simple. And ruby lets me write
things like "2004.to_roman".

The pleac project is also a great way to compare phyton and ruby, if
somebody is still interrested in this.

Karsten Meier
 
A

angus

Hello Romans :)
I have done a solution to work with romans for the pleac cookbook
project, see:

http://pleac.sourceforge.net/pleac_ruby/numbers.html
section "Working with Roman Numerals"

It does also the opposite conversation. I have looked at other solutions
in other languages before writing this, and was quite happy to came up
with this algorithm, because it is so simple. And ruby lets me write
things like "2004.to_roman".

Hi,

A trivial readability improvement:

Instead of

for entry in @@romanlist
sym, num = entry[0], entry[1]

I think it's better to write

for sym, num in @@romanlist

Bye.
 

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

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top