Rabu, 18 Mei 2011

Para escribir cheques (II)


Hace un par de días (aquí) escribí sobre un programa que transforma una cifra en números a letras.La idea es que ponía una cifra: 2347, por ejemplo, y el programa debía darme como resultado "dos mil trescientos cuarenta y siete pesos". Puse en el artículo mencionado mi primera solución en Prolog, pero he aquí que un avezado lector, buen  programador, mejor amigo y técnicamente infalible, Ernesto Blum, me dijo que no estaba considerando algunos casos.

Hoy que fui a dar mi clase, expuse el código fuente y al irlo escribiendo en el pizarrón hallé que algo andaba mal. Así pues, decidí que al llegar a casa re-escribiría el problema para darle una solución definitiva. En un rato hallé las dificultades y según yo, ya las resolví. He aquí mi código fuente:

/********************************************/
/* Programa que traduce de números a letras */
/* Versión 1.1   */
/*            18 mayo 2011     */
/*      Programó: La Morsa   */
/********************************************/
 
predicates

equiv(integer,symbol)
pasa_num_a_letras(integer)


clauses
equiv(1,un).
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(21,veintiuno).
equiv(22,veintidos).
equiv(23,veintitres).
equiv(24,veinticuatro).
equiv(25,veinticinco).
equiv(26,veintiseis).
equiv(27,veintisiete).
equiv(28,veintiocho).
equiv(29,veintinueve).
equiv(30,treinta).
equiv(40,cuarenta).
equiv(50,cincuenta).
equiv(60,sesenta).
equiv(70,setenta).
equiv(80,ochenta).
equiv(90,noventa).
equiv(100,ciento).
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) :- /* condicion terminales */
X < 30,
equiv(X,Resultado),
write(Resultado).


/* predicados recursivos */

pasa_num_a_letras(Cifra) :-
Cifra div 1000 <> 0, /* la cifra tiene diez miles */
DMiles = Cifra div 1000,
DMiles > 30,
pasa_num_a_letras(DMiles), /*doble recursion */
write(" mil "),
Miles = Cifra - (DMiles * 1000),
pasa_num_a_letras(Miles).

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) :-
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).






Cabe señalar que el programa está escrito en Turbo Prolog 2.0 y que se está usando el tamaño de variable entera para el procesamiento de la cifra, por ende, solamente puede resolver el problema para números enteros mayores a cero y menores a 32768, que es el límite de un entero. Entre otras cosas, este código ya me dejó más contento porque solamente contiene una salida de la función recursiva y eso me parece más "elegante", valga la expresión.

Igualmente, expresiones como: 
 
Cientos = Cifra - (Miles * 1000),
 
 
Podrían ponerse como

Cientos = Cifra mod 1000

Hice algunas pruebas y parece que ahora sí todo funciona bien.

A todo esto, el mismo Ernesto Blum me mandó la solución de este programa en el lenguaje de programación Ruby:


#!/usr/bin/ruby



def cen_out(numero)
uni = numero % 10 / 1
dec = numero % 100 / 10
cen = numero % 1000 / 100
case cen
when 0; return ""
when 1; if uni == 0 && dec == 0
return "cien"
else
return "ciento "
end
when 2; return "doscientos "
when 3; return "trescientos "
when 4; return "cuatrocientos "
when 5; return "quinientos "
when 6; return "seiscientos "
when 7; return "setecientos "
when 8; return "ochocientos "
when 9; return "novecientos "
end
end



def dec_out(numero)
uni = numero % 10 / 1
dec = numero % 100 / 10
if dec == 1
case uni
when 0; return "diez"
when 1; return "once"
when 2; return "doce"
when 3; return "trece"
when 4; return "catorce"
when 5; return "quince"
when 6; return "dieciseis"
when 7; return "diecisiete"
when 8; return "dieciocho"
when 9; return "diecinueve"
end
elsif uni == 0
case dec
when 0; return ""
when 1; return ""
when 2; return "veinte"
when 3; return "treinta"
when 4; return "cuarenta"
when 5; return "cincuenta"
when 6; return "sesenta"
when 7; return "setenta"
when 8; return "ochenta"
when 9; return "noventa"
end
else
case dec
when 0; return ""
when 1; return ""
when 2; return "veinti"
when 3; return "treinta y "
when 4; return "cuarenta y "
when 5; return "cincuenta y "
when 6; return "sesenta y "
when 7; return "setenta y "
when 8; return "ochenta y "
when 9; return "noventa y "
end
end
end




def uni_out(numero)
uni = numero % 10 / 1
dec = numero % 100 / 10
if dec == 1
return ""
else
case uni
when 0; return ""
when 1; return "un"
when 2; return "dos"
when 3; return "tres"
when 4; return "cuatro"
when 5; return "cinco"
when 6; return "seis"
when 7; return "siete"
when 8; return "ocho"
when 9; return "nueve"
end
end
end



def letra(numero)
return cen_out(numero), dec_out(numero), uni_out(numero), "\n"
end

for n in 1..1000
print letra(n)
end 
 

Por el momento así las cosas. Cualquier cambio o problema con el código mío, lo pondré en el blog en su oportunidad.

Tidak ada komentar:

Posting Komentar