domingo, 1 de mayo de 2011

Python: Numeros decimales a romanos

La semana pasada publiqué un pequeño programa en Python que convierte números romanos a decimales, así que ayer, apenas pasada la medianoche, me puse a pensar y programar la contraparte.

Este programa fue levemente más complicado de pensar, debido a la complicación añadida por el sistema de numeración romano que incluye algunas particularidades para los números 4 y 9, 40 y 90, etc.

Las soluciones posibles son, a grandes rasgos, dos: armar una rutina especial para todo lo que incluya 4 o 9, o hacer un uso más creativo del diccionario de datos y declarar los casos especiales además de los casos comunes.

Debido a la baja cantidad de casos especiales, considerando que el sistema romano básico llega solo desde 1 hasta 3999, he elegido el método de agregar datos en el diccionario. Es levemente más trabajo incluir esos datos ahí, y la simplificación del código es evidente.

Como adicionales, el programa intenta usar un controlador de expresiones regulares para verificar que lo ingresado sea numérico, y verifica que el dato ingresado sea menor que 4000, ya que es muy dificil representar en una linea de comando los números que siguen.

Para que se den una idea, los romanos usaban una raya arriba de sus numerales para indicar multiplicación por mil, dos rayas para un millon. Si bien el sistema es relativamente simple de implementar a nivel programación, la salida de dato con consola está pensada para no depender de ningún sistema operativo en particular, por lo que es casi imposible implementar ese tipo de linea superior en forma que sea coherente.

#!/usr/bin/python
import sys, re

def checks(argum,patron):
#http://docs.python.org/library/re.html
#re.search returns None if no position in the string matches the pattern
if re.search(patron, argum):
print 'Error: lo ingresado no es un numero decimal'
sys.exit(2)


arg = len(sys.argv)
if arg == 1:
print 'Modo de uso: Dec2Roma.py numero_decimal menor a 4000'
sys.exit(1)

roma = {1000: 'M',900:'CM',500:'D',400:'CD', 100:'C',90:'XC', 50:'L',40:'XC',10: 'X',9:'IX',5: 'V',4:'IV',1:'I'}
claves = sorted((roma.keys()))
claves.reverse()

patron = r'[^\0-9]'
checks(sys.argv[1],patron)

numero = int(sys.argv[1])
if numero >= 4000:
print 'Error: numero demasiado grande para conversion'
sys.exit(3)
salida = ''

for clave in claves:
while clave <= numero:
salida = salida + roma[clave]
numero = numero - clave
print salida


Noten que debido al ancho del blog la linea del diccionario está dividida en dos lineas, pero realmente es una sola linea. Voy a tener que buscar alguna otra alternativa para postear código para que quede presentable.

Otro detalle interesante es que además de tener el diccionario para la búsqueda, podemos usar el método keys() del diccionario para extraer las claves, que luego de ser ordenadas en forma decreciente pueden ser usadas para el bucle principal del programa, lo que simplifica mucho la declaración de datos.

Para que lo entiendan los interesados en como funciona la programación en forma interna, en Python todo es un objeto. Esto provoca que al definir algo, este algo sea incorporado a un tipo de datos en particular, o sea agregado como instancia de un tipo de objeto en particular. En este caso roma es un diccionario, o sea es una instancia del objeto diccionario. Es por eso que tan solo con declarar el diccionario este hereda todos sus métodos en forma transparente para el programador.

Como verán, es a través de la implementación de pequeños programas triviales que avanzamos en el dominio de cualquier lenguaje de programación. Cada vez usamos algo más, cada vez queremos probar algo más, y así es como vamos armando de a poco una idea de la totalidad del lenguaje en nuestras cabezas. No es tan difícil, solo es cuestión de empezar en algún punto.

No hay comentarios:

Publicar un comentario