Roman Numerals (Arrgh!)

I have a homework assignment where I have to convert an inputted integer
into Roman Numerals. I was doing fine building upon the previous steps
until the arrays came in. We have to use the format/guidelines the
professors give us or we get points counted off. I’ve looked at so many
ways to do this program through Google, but none of the examples I’ve
seen are even close to what the professor wrote out.

Basically, in my code I have to use the array $numerals and create pairs
with the integer and its corresponding Roman Numeral.

What I can’t figure out how to do is say something like…

For each 10 of the inputted number put an X

I was thinking something along the lines of dividing the inputted number
by the decimal and taking the remaining integer and multiplying the
Roman Numeral by that integer.

Like 30/10 = 3 so “X” * 3 would print out XXX

But I have no idea how to do that with the $numerals array.

How do I get my program to say divide the inputted number by the decimal
part of the pair and then give the numeral part that many times?

def roman_numeral(number)

$numerals = [[10, “X”],
[5, “V”],
[1, “I”]]

result = “”

$numerals.each() do |decimal, numeral|

result = new_variable
end

return result
end

puts “Enter a number to be converted to Roman Numerals.”
my_number = gets().chomp().to_i()
puts roman_numeral(my_number)

Hi, Rick,

 I'm not gonna give you the COMPLETE answer to your homework, but 

think
along this line:

  1. Split the input number on Non_Word_Boundary (basically split on
    NOTHING)

arrNumber_of_Digits = gets().chomp().to_s.split //

 This should give your on the digits you have each individually; 

meaning
how many Ones, how many Tens, and how many Hundreds…

And the build your strRoman = "X" * (the corresponding no of digits)
  1. Also you will need validate what your professor is going to input in
    the
    first place is an integer to begin with, with something like this

puts “Invalid input” unless ARGV[0]=~ /^\d+$/

Well, considering that you at least started thinking on the problem and
did some research, I am going to give you a solution. With a twist, of
course – it uses “advanced” array’s method inject() that I wouldn’t
dare showing to your professors without being able to explain it. So
work through it and simplify for your homework:

NOTE: The following code does not take into account the “subtraction
rule”.

$numerals = [[1000, “M”], [500, “D”], [100, “C”], [50, “L”], [10, “X”],
[5, “V”], [1, “I”]]

def roman_numeral(number)
$numerals.inject([“”, number]) { |result, numeral|
[ result[0] + numeral[1] * (result[1] / numeral[0]), number %
numeral[0] ]
}.first
end

p roman_numeral(1) # => “I”
p roman_numeral(4) # => “IIII”
p roman_numeral(5) # => “V”
p roman_numeral(6) # => “VI”
p roman_numeral(10) # => “X”
p roman_numeral(19) # => “XVIIII”
p roman_numeral(21) # => “XXI”
p roman_numeral(180) # => “CLXXX”
p roman_numeral(949) # => “DCCCCXXXXVIIII”

On Nov 10, 2009, at 3:56 PM, Rick B. wrote:

What I can’t figure out how to do is say something like…

my_number = gets().chomp().to_i()
puts roman_numeral(my_number)

Posted via http://www.ruby-forum.com/.

Sincerely,
Gennady B.

Software Developer 4
Quest Software, Inc.
Aliso Viejo, CA

On Nov 10, 2009, at 6:56 PM, Rick B. wrote:

I have a homework assignment where I have to convert an inputted
integer
into Roman Numerals. I was doing fine building upon the previous steps
until the arrays came in. We have to use the format/guidelines the
professors give us or we get points counted off. I’ve looked at so
many
ways to do this program through Google, but none of the examples I’ve
seen are even close to what the professor wrote out.

Well, as you might have guessed, most of us on the list don’t want to
DO your homework for you, but will give you a bit of guidance to
help to find your own way.

number
by the decimal and taking the remaining integer and multiplying the
Roman Numeral by that integer.

That should work.

Like 30/10 = 3 so “X” * 3 would print out XXX

Yeah, there you go.

 [5, "V"],
 [1, "I"]]

I assume this is representative of the array your professor gives.

Since it is global, you should define it outside of your method.

result = “”

$numerals.each() do |decimal, numeral|

this seems like a good start

so if decimal is greater than number, you need to use numeral based on
the quotient you identified earlier.

quotient = number / decimal

result = new_variable

You want to tack on a new bit of roman numeral here, so look at
String#<<

result << new_variable

and, before you loop to the next pair, you need to account for the
fact that number has been partially expressed in numerals.

Think about how you might have explained the steps to an 8 year old.
(knowing addition, subtraction, and ordering, but not division) Does
that give you a way to think about the algorithm?

Good luck,

-Rob

P.S. I saw Gennady’s post, but I’d argue that no one would really want
to solve the problem that way. And Nik’s way doesn’t give any help
with the non-decimal V, L, or D.

Rob B. http://agileconsultingllc.com
[email protected]

On Nov 10, 2009, at 5:37 PM, Rob B. wrote:

On Nov 10, 2009, at 6:56 PM, Rick B. wrote:

I have a homework assignment where I have to convert an inputted
integer
into Roman Numerals. I was doing fine building upon the previous steps

P.S. I saw Gennady’s post, but I’d argue that no one would really want
to solve the problem that way. And Nik’s way doesn’t give any help

Why not that way? It works and it is short :wink:

Gennady.

I have to say Grennady’s way is quite elegant; I didn’t even think along
that line–Rick wanted “each individual digits”–so I just provided that
via
Regex w/o even thinking about V L D :slight_smile:

My bad…

Thanks guys.

I wasn’t really asking for anyone to “DO” it for me. I just really
didn’t know what to do next. I actually like figuring things out on my
own and won’t ask for help until the point that it’s driving me crazy.

And using Rob’s advice I’m to the part now where I have to divide up the
places.

Instead of dividing 16 by 10s, 5s, and 1s all at once.

I get XVVVIIIIIIIIIIIIIIII instead of XVI.

I’m thinking I can just take a pair out of the array every loop until
the number hits 0.

On Nov 10, 2009, at 8:49 PM, Gennady B. wrote:

P.S. I saw Gennady’s post, but I’d argue that no one would really
want
to solve the problem that way. And Nik’s way doesn’t give any help

Why not that way? It works and it is short :wink:

Gennady.

Not that it doesn’t work, just that it might be easier/quicker to
rewrite than to understand. Short isn’t always a virtue and if
there’s a reason (performance?) to be terse, then there ought to be a
comment by way of explanation.

There are few truly necessary uses of Enumerable#inject and this isn’t
one of them, but if it were, I’d make it at least slightly more
readable:

def roman_numeral(number)
$numerals.inject([“”, number]) { |(answer, number), (arabic,
roman)|
[ answer + roman * (number / arabic), number % arabic ]
}.first
end

And with an appropriately formed $numerals array, the “subtraction
rule” is already covered. :wink:

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Nov 10, 2009, at 6:43 PM, Rob B. wrote:

rewrite than to understand. Short isn’t always a virtue and if
there’s a reason (performance?) to be terse, then there ought to be a
comment by way of explanation.

There are few truly necessary uses of Enumerable#inject and this isn’t

I respectfully disagree. inject() is a very powerful facility that
allows quick elegant implementation of many tasks at hand. I usually
start with an inject()-based implementation (where appropriate) and then
may refactor it later if performance is not satisfactory. The latter
does not happen very often, though. As for the virtue, the less code you
write the fewer bugs get in – especially when you use well tested
standard facilities like inject().

one of them, but if it were, I’d make it at least slightly more
readable:

def roman_numeral(number)
$numerals.inject(["", number]) { |(answer, number), (arabic,
roman)|

For that thanks a lot – somehow I missed the fact it is possible in
ruby.

  [ answer + roman * (number / arabic), number % arabic ]
}.first

end

And with an appropriately formed $numerals array, the “subtraction
rule” is already covered. :wink:

Would you please elaborate? You mean same algorithm and “better”
$numerals would cover mapping of 4 => “IV”, 9 => “IX”, etc.?

On Nov 10, 2009, at 10:30 PM, Gennady B. wrote:

want

There are few truly necessary uses of Enumerable#inject and this
isn’t

I respectfully disagree. inject() is a very powerful facility that
allows quick elegant implementation of many tasks at hand. I usually
start with an inject()-based implementation (where appropriate) and
then may refactor it later if performance is not satisfactory. The
latter does not happen very often, though. As for the virtue, the
less code you write the fewer bugs get in – especially when you use
well tested standard facilities like inject().

Well, I won’t argue the fact that inject is a powerful abstraction. I
will argue that it would be much easier to inject a bug (pun
intended :wink: into your original terse code than something that is a
bit longer, but tries to do less at each step. Changing/swapping array
subscripts, for example.

-Rob

Yes, that’s it exactly. Testing/using 9 before 5, you find IX (and
stop) before you go to VIIII. You can’t repeat one of the “subtraction
groups” the way that you can repeat the individual numerals: 8 is
always VIII and never IVIV or IIX

Rob B. http://agileconsultingllc.com
[email protected]

On Nov 10, 2009, at 10:11 PM, Rick B. wrote:

Instead of dividing 16 by 10s, 5s, and 1s all at once.

I get XVVVIIIIIIIIIIIIIIII instead of XVI.

I’m thinking I can just take a pair out of the array every loop until
the number hits 0.

This opens up a great sidebar opportunity to talk about debugging as a
skill to be developed.

Look at your output. Can you see why it prints what it does?
It starts out correctly with an X, but what should be happening next?
(Think about how that 8 year old would accomplish the conversion.)

I think that your last sentence means that you’re on the right track.

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Wed, Nov 11, 2009 at 3:51 PM, Gennady B.
[email protected] wrote:

-Rob

Yes, that’s it exactly. Testing/using 9 before 5, you find IX (and
stop) before you go to VIIII. You can’t repeat one of the “subtraction
groups” the way that you can repeat the individual numerals: 8 is
always VIII and never IVIV or IIX

Now you intrigued me ;-). So I am asking directly – given the following $numerals:
$numerals = [[1000, “M”], [500, “D”], [100, “C”], [50, “L”], [10, “X”], [5, “V”], [1, “I”]]

$numerals = [[1000, “M”], [900, “CM”], [500, “D”], [400, “CD”], [100,
“C”], [90, “XC”], [50, “L”], [40, “XL”], [10, “X”], [9, “IX”], [5,
“V”], [4, “IV”], [1, “I”]]

…how one can transform it so that the original algorithm would produced shortened roman numbers? Please present the exact value of $numerals satisfying the above condition.

Gennady.


Paul S.
http://www.nomadicfun.co.uk

[email protected]

On Nov 11, 2009, at 5:35 AM, Rob B. wrote:

Yes, that’s it exactly. Testing/using 9 before 5, you find IX (and
stop) before you go to VIIII. You can’t repeat one of the “subtraction
groups” the way that you can repeat the individual numerals: 8 is
always VIII and never IVIV or IIX

Now you intrigued me ;-). So I am asking directly – given the following
$numerals:
$numerals = [[1000, “M”], [500, “D”], [100, “C”], [50, “L”], [10, “X”],
[5, “V”], [1, “I”]]

…how one can transform it so that the original algorithm would
produced shortened roman numbers? Please present the exact value of
$numerals satisfying the above condition.

Gennady.

On Nov 11, 2009, at 10:56 AM, Paul S. wrote:

$numerals would cover mapping of 4 => “IV”, 9 => “IX”, etc.?

…how one can transform it so that the original algorithm would
produced shortened roman numbers? Please present the exact value of
$numerals satisfying the above condition.

Gennady.

Paul S.
http://www.nomadicfun.co.uk

[email protected]

Oh, sorry. I meant “What Paul said”

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Nov 11, 2009, at 10:51 AM, Gennady B. wrote:

Yes, that’s it exactly. Testing/using 9 before 5, you find IX (and
…how one can transform it so that the original algorithm would
produced shortened roman numbers? Please present the exact value of
$numerals satisfying the above condition.

Gennady.

[[1000, “M”], [900, “CM”], [500, “D”], [400, “CD”], [100, “C”], [90,
“XC”], [50, “L”], [40, “XL”], [10, “X”], [9, “IX”], [5, “V”], [4,
“IV”], [1, “I”]]

Q.E.D. (I think that’s Latin for “So there!” :wink:

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Wed, Nov 11, 2009 at 4:03 PM, Rob B.
[email protected] wrote:

always VIII and never IVIV or IIX

[email protected]

Oh, sorry. I meant “What Paul said”

No worries! It is quite beautiful in fact, and very surprising when
you haven’t thought about it. But, if there are no thousands left to
deal with, and you do have a 900 left, there’s only one way to
represent it. If you have less than 900 remaining, you have to drop
to 500 and build up again :slight_smile:


Paul S.
http://www.nomadicfun.co.uk

[email protected]

On Nov 11, 2009, at 7:56 AM, Paul S. wrote:

Now you intrigued me ;-). So I am asking directly – given the following $numerals:
$numerals = [[1000, “M”], [500, “D”], [100, “C”], [50, “L”], [10, “X”], [5, “V”], [1, “I”]]

$numerals = [[1000, “M”], [900, “CM”], [500, “D”], [400, “CD”], [100,
“C”], [90, “XC”], [50, “L”], [40, “XL”], [10, “X”], [9, “IX”], [5,
“V”], [4, “IV”], [1, “I”]]

Oops, true. No morning coffee takes its dues :wink: – I suspected that’s
what was implied, however somehow started thinking that the 2-character
strings might be multiplied by that algorithm. My bad, it works indeed.

Thank you guys,
Gennady.

On Wed, Nov 11, 2009 at 4:17 PM, Gennady B.
[email protected] wrote:

Oops, true. No morning coffee takes its dues :wink: – I suspected that’s what was implied, however somehow started thinking that the 2-character strings might be multiplied by that algorithm. My bad, it works indeed.

Yes indeed, you can’t get doubles of the 2 character strings, because
their values are always more than half of the preceeding value. In
other words, inorder to get a “CMCM” type mistake, your value must be
at least 1800. But if it’s at least 1800, you’d still be processing
the M’s.


Paul S.
http://www.nomadicfun.co.uk

[email protected]

Well, being student as well, I’d love to study Ruby instead of this
(****)
Java. (Where are you studying?) But that’s not the point.

So, here is my piece of advice:

Using $numerals = [
[1000, “M”], [900, “CM”], [500, “D”], [400, “CD”],
[100, “C”], [90, “XC”], [50, “L”], [40, “XL”],
[10, “X”], [9, “IX”], [5, “V”], [4, “IV”],
[1, “I”]
]

,you should iterate on each of them, seeing how much your number have of
this kind (/)
and what is the rest after that, to use it for the next step(%)…
(if you use Array#each, you’ll get a new array(let’s say a) like a=[dec,
romanString]. You can then take them separetely by a[0] and a[1].
Even if this method is not that beautiful in Ruby, it’s the easiest way
to
understand how to do it).

That the way used in a very known book of Ruby.

You just need to manage how to save this in your String at each step.
(You can multiply a String by “X” * 3, and concatenate using << (or +=))

Give us your script when you’ll got one working :wink:

Hope you’ll find this tip usefull,

Benoit

2009/11/11 Paul S. [email protected]

I didn’t miss it at all, it’s quite cool, but very not easy to find
directly
and to explain for an homework …

But I admit it’s quite a nice solution, not that hard to understand.

2009/11/11 Gennady B. [email protected]