Monday, May 16, 2011

Para escribir cheques


Prolog es uno de los lenguajes más fascinantes que hayan sido creados. Basado en el paradigma de la programación lógica, en donde el programador describe el problema y Prolog, a través de su algoritmo de resolución y unificación,  llamado algoritmo de Roberts, resuelve la dificultad planteada.

A diferencia de los lenguajes tradicionales de cuarta generación como Pascal o C, Prolog pide al programador definir las condiciones iniciales y de frontera, así como las rutinas que describen lo que queremos que el programa haga para hallar una solución. En términos generales, Prolog es una especie de "case" gigantesco, como el de los lenguajes más comunes, con la diferencia de que siempre actúa igual, siguiendo un orden determinado de acuerdo a la posición física de las cláusulas en el programa.

Además, Prolog hace énfasis en la recursión, lo cual hace que los programas sean por demás elegantes. Ya lo decía Niklaus Wirth (el inventor de Pascal): "iteratum humanum est, recursivitum divinum est", lo cual es algo así como "iterar es de humano, hacer recursión es de dioses".

Pues bien, hoy en mi clase de Programación Lógica y Funcional les dije a mis alumnos que resolveríamos un problema típico: el de pasar una cifra numérica a su equivalente en texto. Así, si el sistema recibe 2345 deberá dar como resultado "dos mil trescientos cuarenta y cinco". Este tipo de transformaciones hay que hacerlas comúnmente cuando escribimos, por ejemplo, un cheque.

Pues bien, me senté a escribir el programa en cuestión, que había bosquejado en la clase y en el que me comprometí a diseñar y mostrarles en la siguiente clase, que será el próximo miércoles. He aquí el código en turbo Prolog 2.0:



/********************************************/
/* Programa que traduce de números a letras */
/*                Versión 1.0               */
/*                16 mayo 2011              */
/*             Programó: La Morsa           */
/********************************************/

predicates

equiv(integer,symbol)
pasa_num_a_letras(integer)


clauses
equiv(1,uno).
equiv(2,dos).
equiv(3,tres).
equiv(4,cuatro).
equiv(5,cinco).
equiv(6,seis).
equiv(7,siete).
equiv(8,ocho).
equiv(9,nueve).
equiv(10,diez).
equiv(11,once).
equiv(12,doce).
equiv(13,trece).
equiv(14,catorce).
equiv(15,quince).
equiv(16,dieciseis).
equiv(17,diecisiete).
equiv(18,dieciocho).
equiv(19,diecinueve).
equiv(20,veinte).
equiv(30,treinta).
equiv(40,cuarenta).
equiv(50,cincuenta).
equiv(60,sesenta).
equiv(70,setenta).
equiv(80,ochenta).
equiv(90,noventa).
equiv(100,cien).
equiv(200,doscientos).
equiv(300,trescientos).
equiv(400,cuatrocientos).
equiv(500,quinientos).
equiv(600,seiscientos).
equiv(700,setecientos).
equiv(800,ochocientos).
equiv(900,novecientos).
equiv(1000,mil).

pasa_num_a_letras(X) :-   /* condiciones terminales */
   X < 10, 

   equiv(X,Resultado), 
   write(Resultado, " pesos. " ),nl. 

/* predicados recursivos */ 

pasa_num_a_letras(Cifra) :- 
   Cifra div 1000 <> 0, /* la cifra tiene miles */
   Miles = Cifra div 1000,
   equiv(Miles,Resultado),
   write(Resultado, " mil "),
   Cientos = Cifra - (Miles * 1000),
   pasa_num_a_letras(Cientos).

pasa_num_a_letras(Cifra) :-
   Cifra div 100 <> 0, /* la cifra tiene cientos */
   Cien = Cifra div 100,
   Cien1 = Cien * 100,
   equiv(Cien1,Resultado),
   write(Resultado," "),
   Decenas = Cifra - (Cien * 100),
   pasa_num_a_letras(Decenas).

pasa_num_a_letras(Cifra) :- /* unidades entre 11 y 20 */
    Cifra > 9,
    Cifra < 21, 

    equiv(Cifra,Resultado), 
    write(Resultado," pesos"). 

pasa_num_a_letras(Cifra) :- 
   Cifra div 10 <> 0, /* la cifra tiene decenas */
   Dec = Cifra div 10,
   Dec1 = Dec * 10,
   equiv(Dec1,Resultado),
   write(Resultado," y "),
   Unidades = Cifra - (Dec * 10),

   pasa_num_a_letras(Unidades).

4 comments:

Ernesto said...

Creo que no consideraste el caso del 21 al 29.

Saludos!

Morsa said...

Oops... creo que me olvidé eso. Lo revisare. Gracias, Ernesto.

Ernesto said...

Cuando tenía unos 17 años programé esto en Xbase, creo que así se llamaba el lenguaje de DBase III.

Lo verdaderamente difícil fue hacer las pruebas para ver que todos los casos se cumplían.

Después lo lo hice en excel, para hacer facturas para el negocio de un amigo.

La verdad me siento orgulloso de la versión de excel.

Ernesto said...

Hola!

Te envíe una version no recursiva en ruby por correo.

La verdad, este problema en particular se me hace muy complicado de plantear en forma recursiva.

Esto debido a que el texto que debe ir, depende no solo de un dígito en particular sino de la combinación de estos.

Saludos,

Ernesto