Números Romanos

Introducción

Con el propósito de practicar TDD, esta es una de las katas más avanzadas que puedes hacer. A estas alturas ya deberías dominar las habilidades básicas de TDD - las 3 Leyes de TDD, la Regla de Tres para la duplicación, y deberías tener experiencia en cómo estructurar una prueba unitaria (Arrange, Act, Assert).

Este ejercicio te permitirá perfeccionar el proceso para la toma de decisiones sobre cómo añadir tests. Si se hace correctamente, deberías poder hacer evolucionar el algoritmo dando pasos muy pequeños.

Hacerlo de esta manera es una forma segura de codificar: si das un paso equivocado, es más fácil revertirlo en tu software de control de versiones y empezar de nuevo. El "coste" de hacer esto es mínimo y, por lo general, es más fácil retroceder y replantearse las cosas que dedicar tiempo a depurar el código para averiguar en qué ha fallado. Los desarrolladores que dominan TDD generalmente pasan mucho menos tiempo depurando (debugging) código que simplemente deshaciendo sus cambios recientes y probando con otra estrategia.

El rápido feedback que se obtiene de un pequeño cambio significa que se puede reaccionar inmediatamente, en lugar de enfrentarse a un problema que se introdujo en algún momento anterior, donde los cambios subsiguientes anteriores a la detección del problema pueden dificultar su aislamiento. Con progresiones pequeñas, si se rompe algo debería ser muy fácil aislar dónde se ha ido mal.

 Lo recomendable es que apoyes tus decisiones de progresar usando una heurística como la Premisa de Prioridad de Transformación (TPP) (ver la sección "Artículos" más abajo). Se trata de un enfoque desarrollado por Robert C. Martin (Uncle Bob) para facilitar el TDD.

Las transformaciones se definen mejor si se comparan con las refactorizaciones. Las refactorizaciones son cambios en el código que NO cambian su comportamiento. Las transformaciones son cambios en el código que SÍ cambian el comportamiento del código, pero en el contexto de TDD son cambios que permiten que el test (actual) que está fallando pase. Es decir, es código que se escribe para que el código pase de Rojo a Verde.

En física, la Segunda Ley de la Termodinámica define una flecha de progresión donde la entropía se mueve de menos a más. En TDD, hay una flecha de dirección donde las pruebas progresan de más específicas a más genéricas. Al definir las reglas de Premisa de Prioridad de Transformación, Uncle Bob ha definido una serie de transformaciones en orden. Las transformaciones que encabezan la lista son las más sencillas. Se vuelven progresivamente más complejas a medida que descendemos en la lista. Al hacer referencia y aplicar estos tipos de transformación, puedes hacer que tu conjunto de pruebas pase de ser específico a ser más genérico de forma lenta e incremental.

Este es un gran ejercicio para practicar el diseño de algoritmos dirigido por pruebas porque puedes empezar a familiarizarte con el uso de transformaciones para avanzar, pero al mismo tiempo la dirección del viaje no es completamente desconocida. Las reglas para los números romanos (ver más abajo) sugerirían una estrategia básica, pero puedes intentar usar TPP para ayudarte a elegir el siguiente paso.

Es relativamente fácil saber cuál es el siguiente paso, pero el objetivo del ejercicio es intentar que el TPP confirme tus pasos al avanzar. Se aconseja hacer esta kata primero eligiendo tus propios movimientos sin ayuda, y luego repitiéndola usando el TPP para guiarte.

Instrucciones

Escribe un programa para convertir números arábigos en sus números romanos equivalentes.

Los romanos escribían sus números utilizando combinaciones de las siguientes letras:

Number Numeral
1 I
5 V
10 X
50 L
100 C
500 D
1000 M

 

Inicialmente, el sistema de números romanos consistía en expresar el número mediante el símbolo más apropiado que pudiera utilizarse.

Para empezar, para los números del 1 al 4, se utilizaba el símbolo "I" en los múltiplos para representar el número:

Number Numeral
1 I
2 II
3 III
4 IV

 

Cuando los números llegaban a 5, utilizábamos la 'V', ya que este símbolo era la forma más eficaz de representar el número.

Número Numeral
5 V

 

Para los enteros sencillos superiores a 5, entró en vigor una nueva regla: cuando se añaden símbolos "menores" una o más veces detrás del símbolo "mayor", se considera que se añade al valor que representa el símbolo mayor.

Número Numeral
6 VI
7 VII
8 VIII
9 VIIII

 

So, the initial sequence of Roman Numerals for the integers 1 - 9 were as follows:

1 2 3 4 5 6 7 8 9
I II III IIII V VI VII VIII VIIII

 

Sin embargo, como habrás notado, los números para el 4 y el 9 no están bien. Pero la formación original de los numerales era ésta (incluso algunas esferas de reloj siguen mostrando IIII para el 4). Pero esto creó problemas, ya que la gente seguía confundiendo el III con el IIII y el VIII con el VIIII, por lo que se desarrolló una regla adicional.

Para números como el 4 y el 9, el símbolo menor (I) se antepone al símbolo mayor (V o 5 para el número 4, y X que representa el 10 para el número 9). Cuando el símbolo menor aparece antes del mayor, se considera restado del mayor.

Así, 'IV' se evalúa como: V - I = 5 - 1 = 4 y 'IX' se evalúa como: X - I = 10 - 1 = 9

Por lo tanto, la lista de números romanos del 1 al 9 se modifica como sigue:

1 2 3 4 5 6 7 8 9
I II III IV V VI VII VIII IX

 

Dado que los números romanos siguen un sistema de notación decimal, se aplican las mismas reglas para el siguiente orden: unidades de 10. En este caso, utiliza los símbolos "X" para 10, "L" para cincuenta y "C" para cien. 

10 20 30 40 50 60 70 80 90
X XX XXX XL L LX LXX LXXX XC

 

Para el siguiente orden decimal, utilice las mismas reglas con "C" para 100, "D" para 500 y "M" para 1000.

100 200 300 400 500 600 700 800 900
C CC CCC CD D DC DCC DCCC CM

 

(Como no hay símbolos superiores a "M" (1000), el patrón se detiene aquí, y normalmente los números romanos no consideran números superiores a unos pocos miles).

Los numerales siguen ciertas reglas que deben respetarse:

  • Los símbolos de "base 1" ("I", "X", "C", "M") pueden restarse del siguiente símbolo más alto de "base 5" ("V", "L", "D") o de "base 1", pero sólo se permite una vez. El símbolo no se puede anteponer a un símbolo que esté en el orden decimal inmediatamente superior. Por lo tanto, "IV", "IX" está bien, pero "IL" o "IC" no lo están. XL" y "XC" son válidos, pero XD y XM no ("CD" y "CM" también son válidos).

  • Los símbolos "I" y "X" pueden repetirse como máximo 3 veces seguidas cuando se añade el símbolo.

  • Los símbolos "V", "L" y "D" de "base 5" no pueden repetirse nunca.

Más ejemplos:

Número Numeral
4 IV
9 IX
29 XXIX
80 LXXX
294 CCXCIV
2019 MMXIX

Para empezar

Tu solución debería contener un método similar a:

public string Convert(int amount)

 

Aquí, amount representa el número arábigo y el valor que devuelve este método es la cadena de números romanos.

Consejos

  • Avanza poco a poco en pequeños pasos. Siempre debe ser capaz de ir a un estado verde (pasando todas los tests) rápidamente.
  • Cíñete a los pasos rojo, verde y de refactorización.
  • Intenta no refactorizar en rojo. Si escribes una prueba que falla y que necesitaría una gran reescritura para pasar, considera eliminar la prueba, refactorizar en el verde, y luego escribir la prueba de nuevo.
  • A medida que avances en el problema, verás cómo tu algoritmo empieza a evolucionar. En otras palabras, a medida que las pruebas se vuelven más específicas, el código se vuelve más genérico.
  • No tengas miedo a dejar que el código se desordene un poco: a veces es la mejor manera de detectar el siguiente paso de refactorización.

Artículos

Soluciones

Videos