2008-09-02

Roman numerals

Yesterday we had another GothPy meeting. Apart from getting some formalities out of the way, Emily showed us how to do the FizzBuzz kata in just over 4 minutes. Quite impressive. This was one of the things she and Michael Feathers had done for the Programming with the Stars competition at Agile 2008.

We were also supposed to tackle a "new" kata, converting numbers to roman numerals, but a debate about what rules should be used to guide the conversion (for example, should 1999 be 'IMM' or 'MCMXCIX'?) got us into doing an english translation instead (converting numbers to english text). And as the smug Lisp weenie I have turned into I couldn't help but to inform everyone that the built-in format directives ~@R and ~R solves those problems quite neatly. The hard part is of course to write the Lisp implementation ;-)

Anyway, as usual, it turned out we thought about the problem a bit differently but we quickly converged around a "good enough" strategy even though it wasn't particularly pretty and we ended up with lots of code duplication. I learned a lot though. It put my previous solution in a different light and it's always interesting to see and hear how others tackle awkward problems like this.

And BTW. Here is my attempt at the Roman numerals kata (using doctest):

def as_roman_numeral(num):
"""
Returns a string containing the roman numeral representation of num or
None if num is outside of range(1,4000).

>>> as_roman_numeral(4000) == None
True
>>> as_roman_numeral(1)
'I'
>>> as_roman_numeral(99)
'XCIX'
>>> as_roman_numeral(888)
'DCCCLXXXVIII'
>>> as_roman_numeral(1999)
'MCMXCIX'
>>> as_roman_numeral(2001)
'MMI'
"""

units = [0, 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
tens = [0, 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC']
hundreds = [0, 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM']
thousands = [0, 'M', 'MM', 'MMM']

if num in xrange(1,4000):
th, mils = num//1000, num%1000
h, cents = mils//100, mils%100
te, u = cents//10, cents%10
return "".join(numerals[digit] for digit, numerals in [(th, thousands),
(h, hundreds),
(te, tens),
(u, units)] if digit > 0)
else:
return None
It works with numbers in range(1,4000) and it implements the rules as described on Wikipedia.

Inga kommentarer: