Code Kata – RomanNumerals

15 febrero, 2011 por Ana Buigues Dejar una respuesta »

Siguiendo con la iniciativa lanzada por 12meses12katas en la que ya participé el mes pasado realizando mi primera code kata, este mes sigo experimentando en el mundo de las katas. Recomiendo a todo programador, que si tiene un poco de tiempo (las katas se realizan en un tiempo máximo de 2 horas) que prueben la experiencia, ya que estos ejercicios te permiten mejorar la técnica de desarrollo, aplicando TDD, que implica escribir los tests primero, provocar un fallo, implementar la corrección del fallo y refactorizar.

La kata de este mes trata de resolver el problema de las conversiones entre números enteros y romanos.  Para hacer que la kata sea un reto mayor, he decido resolverla en Python que representa un poco más de dificultad que si la realizase en Java, ya que estoy más acostumbrada a programar en Java, En Python soy una principiante,  por lo que seguramente el código escrito para solucionar la kata se podría mejorar, así que sí veís algún fallo no dudeís en comentarlo!

Podeís ver las distintas soluciones que estan aportando a la iniciativa desde el github del proyecto. Os aconsejo que os paseís por ahi para ver la multitud de soluciones que se pueden dar a un mismo problema y la diversidad de lenguajes de programación con la que se resuelven.

A continuación os dejo el código de la kata

class RomanNumerals(object):
    def __init__(self):
        #inicializaciones para la conversión de entero a romano
        self.values =[ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ]
        self.symbols =[ "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" ]
        #inicializaciones para la conversión de romano a entero
        self.value =[ 1000, 500, 100, 50, 10, 5, 1 ]
        self.symbol = "MDCLXVI";
    def intToRoman(self, num):
        '''
            Realiza la conversión de un número entero a romano.
        '''
        output = ""
        index = 0
        #finalizamos cuando el número es 0
        while num > 0:
            while num >= self.values[index]:
                #añadimos el símbolo a la solución
                output = output + self.symbols[index]
                #restamos el valor númerico
                num = num - self.values[index]
            index = index + 1
        return output
    def romanToInt(self, num):
        '''
            Realiza la conversión de un número romano a entero
        '''
        indexSymbol = 0
        output = 0
        index = 0
        valid = True
        lastValue = 0
        while valid and index < len(num):
            #obtenemos el símbolo
            car = num[index]
            #obtenemos el índice del símbolo
            indexSymbol = self.symbol.index(car)
            if(indexSymbol >= 0):
                #sumamos su valor correspondiente
                output = output + self.value[indexSymbol]
                # si el valor es mayor que el último valor, tenemos que restar el último valor sumado
                if self.value[indexSymbol] > lastValue:
                    output = output - 2 * lastValue
                lastValue = self.value[indexSymbol]
            else:
                valid = False
            index = index + 1
        return output

Con su correspondiente test

import unittest
from RomanNumerals import RomanNumerals
class Test(unittest.TestCase):
    def setUp(self):
        self.roman = RomanNumerals()
    def testSimple(self):
        self.checkConversion(1,"I")
        self.checkConversion(4,"IV")
        self.checkConversion(5,"V")
        self.checkConversion(9,"IX")
        self.checkConversion(10,"X")
        self.checkConversion(40,"XL")
        self.checkConversion(50,"L")
        self.checkConversion(90,"XC")
        self.checkConversion(100,"C")
        self.checkConversion(400,"CD")
        self.checkConversion(500,"D")
        self.checkConversion(900,"CM")
        self.checkConversion(1000,"M")
    def testTens(self):
        self.checkConversion(11,"XI")
        self.checkConversion(12,"XII")
        self.checkConversion(13,"XIII")
        self.checkConversion(14,"XIV")
        self.checkConversion(19,"XIX")
        self.checkConversion(20,"XX")
        self.checkConversion(24,"XXIV")
        self.checkConversion(31,"XXXI")
        self.checkConversion(38,"XXXVIII")
        self.checkConversion(45,"XLV")
        self.checkConversion(71,"LXXI")
        self.checkConversion(87,"LXXXVII")
        self.checkConversion(99,"XCIX")
    def testHundreds(self):
        self.checkConversion(109,"CIX")
        self.checkConversion(203,"CCIII")
        self.checkConversion(303,"CCCIII")
        self.checkConversion(304,"CCCIV")
        self.checkConversion(450,"CDL")
        self.checkConversion(546,"DXLVI")
        self.checkConversion(671,"DCLXXI")
        self.checkConversion(788,"DCCLXXXVIII")
        self.checkConversion(888,"DCCCLXXXVIII")
        self.checkConversion(999,"CMXCIX")
    def testThousands(self):
        self.checkConversion(1010,"MX")
        self.checkConversion(1111,"MCXI")
        self.checkConversion(1234,"MCCXXXIV")
        self.checkConversion(2342,"MMCCCXLII")
        self.checkConversion(2999,"MMCMXCIX")
    def checkConversion(self,numeral, roman):
        self.assertEquals(roman, self.roman.intToRoman(numeral))
        self.assertEquals(numeral, self.roman.romanToInt(roman))
if __name__ == "__main__":
    unittest.main()

5 comentarios

  1. Doc dice:

    Hola wapa,

    Vaya!, que buen apinta tiene esta iniciativa … por lo visto le encuentro un poco tarde :) pero me parece una idea muy original para seguir aprendiendo cosas nuevas. Por mucho que se domina un determinado lenguaje de programación, es facil que hayan metodos desconocidos por uno y este tipo de iniciativas pueden ayudar a decubrirlos.

    Saludos!

Deja un comentario