Ruby Cookbook recipe #2.14: Roman Numerals

Posted on January 2, 2007
Filed Under /dev/null/ | 58 views |

In learning Ruby I've been working my way through O'Reilly's Ruby Cookbook and am thoroughly enjoying it (though given that I started at page 557 in the Rails section and am now on page 78 "through" might be the wrong word).

Part of the fun for me in learning a new language via examples like those is taking the examples and tweaking them until they work the way I think they should rather than as they're given. Anyone can copy lines from a book but to improve upon them is to really understand.

The Roman Numerals recipe was particularly interesting because it incorporates so many Ruby-isms that to make a simple one-line code change required a complete understanding of nearly everything about the example and thus in turn learn that much more about Ruby.

In Ruby Cookbook the authors propose this syntax for converting from integers to Roman numerals:

RUBY:
  1. 10.to_roman()

and this syntax for converting from Roman numerals to integers:

RUBY:
  1. 'III'.to_roman().to_arabic()

To my eyes that first method is very elegant and that second one is butt-ugly. Convert a Roman numeral string to a Roman numeral to an integer? Nope. I'd much rather have:

RUBY:
  1. 'III'.to_arabic()

It turns out the change required to do this is really quite simple. One of the coolest features about Ruby is the ability to tag functionality onto existing classes by simply re-defining the class and creating a new method.

In Ruby Cookbook the authors make it possible to call to_roman() and to_roman().to_arabic() on numbers and strings by extending those classes. Of particular interest to me in this case was their extension of String:

RUBY:
  1. class String
  2.   def to_roman
  3.     return Roman.new( self )
  4.   end
  5. end

Ah, they're missing a to_arabic() in there. Instead, they get an instance of the Roman class and call to_arabic() on that. It works but as I said before, it's aesthetically ugly to mine eyes.

Instead, extend String like so:

RUBY:
  1. class String
  2.   def to_roman
  3.     return Roman.new( self )
  4.   end
  5.  
  6.   def to_arabic
  7.     return Roman.new( self ).to_arabic()
  8.   end
  9. end

Ta-da! Now 'IV'.to_arabic() works too, and so does 'IV'.to_roman().to_arabic().

An aside: Ruby convention seems to be to not put return on the line of code that is returned by a method because Ruby automatically returns whatever the result of the last line of a method is. While that's admirable in theory I find the practice of not having any explicitly-defined return point rather odd. I also tend to like my method calls to have parenthesis on them, even if they don't need them. Maybe I'm just old-fashioned but I think it all reads much clearer that way.

Comments

Leave a Reply