Programa que imprime un número entre 0 y un billón como texto [SWI-Prolog]

Iniciado por _TTFH_3500, 8 Octubre 2020, 23:34 PM

0 Miembros y 1 Visitante están viendo este tema.

_TTFH_3500

Siempre quise hacer un programa que dado un numero lo imprima como texto, por ejemplo 132 -> ciento treinta y dos, pero nunca me puse, hasta hoy que vi gramáticas de cláusulas definidas en programación lógica y en poco más de media hora salió.
Los casos más difíciles de hacer fueron por ejemplo 16 (dieciséis), 21 (veintiuno) que son una única palabra y 1002 (mil dos) que no es "un mil dos" o "uno mil dos".
Como no chequeo que el resto sea distinto de cero pueden generarse casos como "40 -> cuarenta y cero" o "41 -> cuarenta y cero y uno, cuarenta y cero... y cero y uno" que son quitados con el operador cut (!).
Les dejo acá el código y algunos ejemplos.
17 diecisiete
29 veintinueve
547 quinientos cuarenta y siete
1234 mil doscientos treinta y cuatro
3597426 tres millones quinientos noventa y siete mil cuatrocientos veintiséis
742258963 setecientos cuarenta y dos millones doscientos cincuenta y ocho mil novecientos sesenta y tres
100000000000 cien mil millones
999999999999 novecientos noventa y nueve mil novecientos noventa y nueve millones novecientos noventa y nueve mil novecientos noventa y nueve
1000000000000 un billon
31000 treinta y uno mil*
* Y aquí encontré un bug, debería decir treinta y un mil

Código (bash) [Seleccionar]

% Imprime un numero como texto
print_number(N) :- numero(N, L, []), !, atomic_list_concat(L, ' ', A), writeln(A).

% Crea una lista [0..N]
create_list(N, L) :- findall(X, between(0, N, X), L).

% Imprime los numeros entre 0 y N
print_numbers(N) :- create_list(N, L), print_aux(L).
print_aux([]).
print_aux([X|L]) :- print_number(X), print_aux(L).

% Convierte un numero en una lista de palabras
% ej: numero(132, L, []) -> [ciento, treinta, y, dos].
numero(0) --> [cero].
numero(1) --> [uno].
numero(2) --> [dos].
numero(3) --> [tres].
numero(4) --> [cuatro].
numero(5) --> [cinco].
numero(6) --> [seis].
numero(7) --> [siete].
numero(8) --> [ocho].
numero(9) --> [nueve].

numero(10) --> [dies].
numero(11) --> [once].
numero(12) --> [doce].
numero(13) --> [trece].
numero(14) --> [catorce].
numero(15) --> [quince].

numero(20) --> [veinte].
numero(30) --> [treinta].
numero(40) --> [cuarenta].
numero(50) --> [cincuenta].
numero(60) --> [sesenta].
numero(70) --> [setenta].
numero(80) --> [ochenta].
numero(90) --> [noventa].
numero(100) --> [cien].

% Convertir numeros entre 16 y 19
numero(N) --> {N > 15, N < 20, U is N mod 10, numero(U, X, []), atomic_list_concat([dieci|X], S)}, [S].
% Convertir numeros entre 21 y 29
numero(N) --> {N > 20, N < 30, U is N mod 10, numero(U, X, []), atomic_list_concat([veinti|X], S)}, [S].
% Convertir numeros entre 31 y 99
numero(N) --> {N > 30, N < 100, D is N - (N mod 10), U is N mod 10}, numero(D), [y], numero(U).

numero(200) --> [doscientos].
numero(300) --> [trescientos].
numero(400) --> [cuatrocientos].
numero(500) --> [quinientos].
numero(600) --> [seiscientos].
numero(700) --> [setecientos].
numero(800) --> [ochocientos].
numero(900) --> [novecientos].
numero(1000) --> [mil].

% Convertir numeros entre 101 y 199
numero(N) --> {N > 100, N < 200, D is N mod 100}, [ciento], numero(D).
% Convertir numeros entre 201 y 999
numero(N) --> {N > 200, N < 1000, C is N - (N mod 100), D is N mod 100}, numero(C), numero(D).

% Convertir numeros entre 1001 y 1999
numero(N) --> {N > 1000, N < 2000, D is N mod 1000}, [mil], numero(D).
% Convertir numeros entre 2000 y 999 000 que terminan en 000, ej: 7000
numero(N) --> {N >= 2000, N =< 999000, C is N div 1000, D is N mod 1000, D = 0}, numero(C), [mil].
% Convertir numeros entre 2001 y 999 999
numero(N) --> {N > 2000, N < 1000000, C is N div 1000, D is N mod 1000}, numero(C), [mil], numero(D).

numero(1000000) --> [un, millon].

% Convertir numeros entre 1 000 001 y 1 999 999
numero(N) --> {N > 1000000, N < 2000000, D is N mod 1000000}, [un, millon], numero(D).
% Convertir numeros entre 2 000 000 y 999 999 000 000 que terminan en 000 000, ej: 7 000 000
numero(N) --> {N >= 2000000, N =< 999999000000, C is N div 1000000, D is N mod 1000000, D = 0}, numero(C), [millones].
% Convertir numeros entre 2000001 y 999 999 999 999
numero(N) --> {N > 2000000, N < 1000000000000, C is N div 1000000, D is N mod 1000000}, numero(C), [millones], numero(D).

numero(1000000000000) --> [un, billon].


Para ejecutar instalan SWI-Prolog
Guardan el código como "numtext.pl"
Abren cmd en el directorio donde guardaron el archivo
> swipl numtext.pl
?- print_number(N). # donde N es el número a mostrar