Tema 5: Programación lógica de orden superior
Índice
- 1. Modificación de la base de conocimiento
- 2. Todas las soluciones
- 3. Procesamiento de términos
- 4. Transformaciones entre átomos y listas
- 5. Procedimientos aplicativos
- 6. Predicados sobre tipos de término
- 7. Comparación y ordenación de términos
- 8. Ejercicios
- 8.1. Lista de los divisores de un número
- 8.2. Reconocimiento de números primos
- 8.3. Generación de números primos
- 8.4. Números amigos
- 8.5. Transformación de listas en conjuntos
- 8.6. Todos los elementos cumple una propiedad
- 8.7. Algún elemento cumple una propiedad
- 8.8. Sublistas de elementos que verifican una propiedad
- 8.9. Rotaciones de una lista
- 8.10. Lista de números capicúas en un intervalo
- 8.11. Palabra inversa
- 8.12. Determinación de un número por su factorial
- 8.13. Nodos de una generación en una lista de árboles binarios
- 8.14. Lista de elementos únicos
- 8.15. Elementos más frecuentes de una lista
- 8.16. El problema 3n+1
- 8.17. Números perfectos
- 8.18. Operación binaria aplicada a listas
- 8.19. Eliminación de las vocales
- 8.20. Palabras maximales
- 8.21. Clausura transitiva
- 8.22. Traducción de dígitos a palabras
- 8.23. Transformación de lista dependiente de la posición
- 8.24. Aplanamiento de listas
- 9. Bibliografía
1. Modificación de la base de conocimiento
1.1. Los predicados assert
y retract
:
assert(+Term)
inserta un hecho o una cláusula en la base de conocimientos.Term
es insertado como última cláusula del predicado correspondiente.retract(+Term)
elimina la primera cláusula de la base de conocimientos que unifica conTerm
.Ejemplos:
?- dynamic hace_frío/0. true. ?- hace_frío. false. ?- assert(hace_frío). true. ?- hace_frío. true. ?- retract(hace_frío). true. ?- hace_frío. false.
1.2. El predicado listing
:
listing(+Pred)
lista las cláusulas en cuya cabeza aparece el predicadoPred
. Por ejemplo,?- assert((gana(X,Y) :- rápido(X), lento(Y))). true. ?- listing(gana). :- dynamic gana/2. gana(A, B) :- rápido(A), lento(B). true. ?- assert(rápido(juan)), assert(lento(jose)), assert(lento(luis)). true. ?- gana(X,Y). X = juan, Y = jose ; X = juan, Y = luis. ?- retract(lento(X)). X = jose ; X = luis. ?- gana(X,Y). false.
1.3. Los predicados asserta
y assertz
:
asserta(+Term)
equivale aassert/1
, peroTerm
es insertado como primera cláusula del predicado correspondiente.assertz(+Term)
equivale aassert/1
.Ejemplos:
?- assert(p(a)), assertz(p(b)), asserta(p(c)). true. ?- p(X). X = c ; X = a ; X = b. ?- listing(p). :- dynamic p/1. p(c). p(a). p(b). true.
1.4. Los predicados retractall
y abolish
:
retractall(+C)
elimina de la base de conocimientos todas las cláusulas cuya cabeza unifica conC
.abolish(+SimbPred/+Aridad)
elimina de la base de conocimientos todas las cláusulas que en su cabeza aparece el símbolo de predicadoSimbPred/Aridad
.Ejemplos:
?- assert(p(a)), assert(p(b)). true. ?- retractall(p(_)). true. ?- p(a). false. ?- assert(p(a)), assert(p(b)). true. ?- abolish(p/1). true. ?- p(a). ERROR: Unknown procedure: p/1 (DWIM could not correct goal)
1.5. Tablas de multiplicar
crea_tabla
añade los hechosproducto(X,Y,Z)
dondeX
eY
son números de 0 a 9 yZ
es el producto deX
eY
. Por ejemplo,?- crea_tabla. Yes ?- listing(producto). producto(0,0,0). producto(0,1,0). ... producto(9,8,72). producto(9,9,81). Yes
Su definición es (tabla_de_multiplicar.pl).
crea_tabla :- L = [0,1,2,3,4,5,6,7,8,9], member(X,L), member(Y,L), Z is X*Y, assert(producto(X,Y,Z)), fail. crea_tabla.
Consulta: Determinar las descomposiciones de 6 en producto de dos números.
?- producto(A,B,6). A = 1, B = 6 ; A = 2, B = 3 ; A = 3, B = 2 ; A = 6, B = 1.
2. Todas las soluciones
2.1. Lista de todas las soluciones
findall(T,O,L)
se verifica siL
es la lista de las instancias del términoT
que verifican el objetivoO
. Por ejemplo,?- assert(clase(a,voc)), assert(clase(b,con)), assert(clase(e,voc)), assert(clase(c,con)). true. ?- findall(X,clase(X,voc),L). L = [a, e]. ?- findall(_X,clase(_X,_Clase),L). L = [a, b, e, c]. ?- findall(X,clase(X,vocal),L). L = []. ?- findall(X,(member(X,[c,b,c]),member(X,[c,b,a])),L). L = [c, b, c]. ?- findall(X,(member(X,[c,b,c]),member(X,[1,2,3])),L). L = [].
2.2. Conjunto de todas las soluciones
setof(T,O,L)
se verifica siL
es la lista ordenada sin repeticiones de las instancias del términoT
que verifican el objetivoO
. Por ejemplo,?- setof(X,clase(X,Clase),L). Clase = con, L = [b, c] ; Clase = voc, L = [a, e]. ?- setof(X,Y^clase(X,Y),L). L = [a, b, c, e]. ?- setof(X,clase(X,vocal),L). false. ?- setof(X,(member(X,[c,b,c]),member(X,[c,b,a])),L). L = [b, c]. ?- setof(X,(member(X,[c,b,c]),member(X,[1,2,3])),L). false.
2.3. Multiconjunto de todas las soluciones
bagof(T,O,L)
se verifica siL
es el multiconjunto de las instancias del términoT
que verifican el objetivoO
. Por ejemplo,?- bagof(X,clase(X,Clase),L). Clase = con, L = [b, c] ; Clase = voc, L = [a, e]. ?- bagof(X,Y^clase(X,Y),L). L = [a, b, e, c]. ?- bagof(X,clase(X,vocal),L). false. ?- bagof(X,(member(X,[c,b,c]),member(X,[c,b,a])),L). L = [c, b, c]. ?- bagof(X,(member(X,[c,b,c]),member(X,[1,2,3])),L). false.
2.4. Operaciones conjuntistas
- Nota: El código del programa que se desarrollará en esta sección se encuentra en conjuntos.pl.
setof0(T,0,L)
es comosetof
salvo en el caso en que ninguna instancia deT
verifiqueO
, en cuyo casoL
es la lista vacía. Por ejemplo,?- setof0(X, (member(X,[c,a,b]),member(X,[c,b,d])), L). L = [b, c]. ?- setof0(X, (member(X,[c,a,b]),member(X,[e,f])), L). L = [].
Su definición es
setof0(X,O,L) :- setof(X,O,L), !. setof0(_,_,[]).
intersección(S,T,U)
se verifica siU
es la intersección deS
yT
. Por ejemplo,?- intersección([1,4,2],[2,3,4],U). U = [2,4].
Su definición es
intersección(S,T,U) :- setof0(X, (member(X,S), member(X,T)), U).
unión(S,T,U)
se verifica siU
es la unión deS
yT
. Por ejemplo,?- unión([1,2,4],[2,3,4],U). U = [1, 2, 3, 4].
Su definición es
unión(S,T,U) :- setof(X, (member(X,S); member(X,T)), U).
diferencia(S,T,U)
se verifica siU
es la diferencia de los conjuntos deS
yT
. Por ejemplo,?- diferencia([5,1,2],[2,3,4],U). U = [1, 5].
Su definición es
diferencia(S,T,U) :- setof0(X,(member(X,S),not(member(X,T))),U).
subconjunto(-L1,+L2)
se verifica siL1
es un subconjunto deL2
. Por ejemplo,?- subconjunto(L,[a,b]). L = [a, b] ; L = [a] ; L = [b] ; L = [].
Su definición es
subconjunto([],[]). subconjunto([X|L1],[X|L2]) :- subconjunto(L1,L2). subconjunto(L1,[_|L2]) :- subconjunto(L1,L2).
subconjuntos(X,L)
se verifica siL
es el conjunto de las subconjuntos deX
. Por ejemplo, Por ejemplo,?- subconjuntos([a,b,c],L). L = [[], [a], [a, b], [a, b, c], [a, c], [b], [b, c], [c]].
Su definición es
subconjuntos(X,L) :- setof(Y,subconjunto(Y,X),L).
3. Procesamiento de términos
3.1. Transformación entre términos y listas:
?T =.. ?L
se verifica siL
es una lista cuyo primer elemento es el functor del términoT
y los restantes elementos deL
son los argumentos deT
. Por ejemplo,?- padre(juan,luis) =.. L. L = [padre, juan, luis]. ?- T =.. [padre, juan, luis]. T = padre(juan,luis).
alarga(+F1,+N,-F2)
se verifica siF1
yF2
son figuras geométricas del mismo tipo y el tamaño de laF1
es el de laF2
multiplicado porN
, donde las figuras geométricas se representan como términos en los que el functor indica el tipo de figura y los argumentos su tamaño. Por ejemplo,?- alarga(triángulo(3,4,5),2,F). F = triángulo(6, 8, 10). ?- alarga(cuadrado(3),2,F). F = cuadrado(6).
Su definición (que está en alarga.pl) es
alarga(Figura1,Factor,Figura2) :- Figura1 =.. [Tipo|Arg1], multiplica_lista(Arg1,Factor,Arg2), Figura2 =.. [Tipo|Arg2]. multiplica_lista([],_,[]). multiplica_lista([X1|L1],F,[X2|L2]) :- X2 is X1*F, multiplica_lista(L1,F,L2).
3.2. Los procedimientos functor
y arg
:
functor(T,F,A)
se verifica siF
es el functor del términoT
yA
es su aridad.arg(N,T,A)
se verifica siA
es el argumento del términoT
que ocupa el lugarN
.Ejemplo:
?- functor(g(b,c,d),F,A). F = g, A = 3. ?- functor(T,g,2). T = g(_8362, _8364). ?- arg(2,g(b,c,d),X). X = c. ?- functor(T,g,3), arg(1,T,b),arg(2,T,c). T = g(b, c, _1542).
4. Transformaciones entre átomos y listas
4.1. La relación name
:
name(A,L)
se verifica siL
es la lista de códigos ASCII de los caracteres del átomoA
. Por ejemplo,?- name(bandera,L). L = [98, 97, 110, 100, 101, 114, 97]. ?- name(A,[98, 97, 110, 100, 101, 114, 97]). A = bandera.
concatena_átomos(A1,A2,A3)
se verifica siA3
es la concatenación de los átomosA1
yA2
. Por ejemplo,?- concatena_átomos(pi,ojo,X). X = piojo
Su definición (que está en concatena_atomos.pl) es
concatena_átomos(A1,A2,A3) :- name(A1,L1), name(A2,L2), append(L1,L2,L3), name(A3,L3).
5. Procedimientos aplicativos
5.1. El procedimiento apply
apply(T,L)
se verifica si es demostrableT
después de aumentar el número de sus argumentos con los elementos deL
; por ejemplo,?- plus(2,3,X). X = 5. ?- apply(plus,[2,3,X]). X = 5. ?- apply(plus(2),[3,X]). X = 5. ?- apply(plus(2,3),[X]). X = 5. ?- apply(append([1,2]),[X,[1,2,3,4,5]]). X = [3, 4, 5].
Definición de
apply
:n_apply(Término,Lista) :- Término =.. [Pred|Arg1], append(Arg1,Lista,Arg2), Átomo =.. [Pred|Arg2], Átomo.
5.2. El procedimientos maplist
maplist(P,L1,L2)
se verifica si se cumple el predicadoP
sobre los sucesivos pares de elementos de las listasL1
yL2
; por ejemplo,?- succ(2,X). X = 3. ?- succ(X,3). X = 2. ?- maplist(succ,[2,4],[3,5]). true. ?- maplist(succ,[0,4],[3,5]). false. ?- maplist(succ,[2,4],Y). Y = [3, 5]. ?- maplist(succ,X,[3,5]). X = [2, 4].
Definición de
maplist
:n_maplist(_,[],[]) :- !. n_maplist(R,[X1|L1],[X2|L2]) :- apply(R,[X1,X2]), n_maplist(R,L1,L2).
6. Predicados sobre tipos de término
var(T)
se verifica siT
es una variable. Por ejemplo,?- var(X1). true.
atom(T)
se verifica siT
es un átomo. Por ejemplo,?- atom(átomo). true.
number(T)
se verifica siT
es un número. Por ejemplo,?- number(123). true. ?- number(-25.14). true.
compound(T)
se verifica siT
es un término compuesto. Por ejemplo,?- compound(f(X,a)). true. ?- compound([1,2]). true.
atomic(T)
se verifica siT
es una variable, átomo, cadena o número. Por ejemplo,?- atomic(átomo). true. ?- atomic(123). true.
suma_segura(X,Y,Z)
se verifica siX
eY
son enteros yZ
es la suma deX
eY
. Por ejemplo,?- suma_segura(2,3,X). X = 5 Yes ?- suma_segura(7,a,X). No ?- X is 7 + a. % [WARNING: Arithmetic: `a' is not a function]
Su definición (que está en suma_segura.pl) es:
suma_segura(X,Y,Z) :- number(X), number(Y), Z is X+Y.
7. Comparación y ordenación de términos
7.1. Comparación de términos:
T1 = T2
se verifica siT1
yT2
son unificables.T1 == T2
se verifica siT1
yT2
son idénticos.T1
\== T2~ se verifica siT1
yT2
no son idénticos.Ejemplos:
?- f(X) = f(Y). X = Y. ?- f(X) == f(Y). false. ?- f(X) == f(X). true. ?- f(X) \== f(X). false.
cuenta(A,L,N)
se verifica siN
es el número de ocurrencias del átomoA
en la listaL
. Por ejemplo,?- cuenta(a,[a,b,a,a],N). N = 3. ?- cuenta(a,[a,b,X,Y],N). N = 1.
Su definición (que está en cuenta.pl) es:
cuenta(_,[],0) :- !. cuenta(A,[B|L],N) :- A == B, !, cuenta(A,L,M), N is M+1. cuenta(A,[_B|L],N) :- % A \== _B, cuenta(A,L,N).
7.2. Ordenación de términos:
T1 @< T2
se verifica si el términoT1
es anterior queT2
en el orden de términos de Prolog.?- ab @< ac. true. ?- 21 @< 123. true. ?- 12 @< a. true. ?- g @< f(b). true. ?- f(b) @< f(a,b). true. ?- [a,1] @< [a,3]. true.
sort(+L1,-L2)
se verifica siL2
es la lista obtenida ordenando de manera creciente los distintos elementos deL1
y eliminando las repeticiones.?- sort([c4,2,a5,2,c3,a5,2,a5],L). L = [2, a5, c3, c4].
8. Ejercicios
8.1. Lista de los divisores de un número
Ejercicio 1. Definir la relación divisores(+N,-L)
que se verifica si L
es
la lista de los divisores del número N
. Por ejemplo,
?- divisores(24,L). L = [1, 2, 3, 4, 6, 8, 12, 24].
Solución
divisores(N,L) :- findall(X,((between(1,N,X), N mod X =:= 0)),L).
8.2. Reconocimiento de números primos
Ejercicio 2. En los apartados de este ejercicio se darán distintas
definiciones de la relación primo(+N)
que se verifica si N
es un número
primo. Por ejemplo,
?- primo(7). Yes ?- primo(9). No
Ejercicio 2.1. Definir primo(X)
que se verifica si el número de divisores de
X
es menor que 3 [Usar la definición anterior de ~divisores~].
Solución
primo(X) :- divisores(X,L), length(L,N), N < 3.
Ejercicio 2.2. Definir primo(X)4 que se verifica si no hay ningún número entre
2 y la raiz cuadrada de ~X
que sea un divisor de X
.
Solución
primo_2(X) :- Y is floor(sqrt(X)), not((between(2,Y,Z), X mod Z =:= 0)).
Ejercicio 2.3. Definir primo(X)
que se verifica si no hay ningún número entre
2 y la raiz cuadrada de X
que sea primo y divida a X
.
Solución
primo_3(X) :- Y is floor(sqrt(X)), not((between(2,Y,Z), primo_3(Z), X mod Z =:= 0)).
La comparación de eficiencia es
?- time(primo(15485867)). % 30,971,750 inferences, 2.074 CPU in 2.075 seconds (100% CPU, 14929796 Lips) true. ?- time(primo_2(15485867)). % 7,872 inferences, 0.003 CPU in 0.003 seconds (100% CPU, 2948049 Lips) true. ?- time(primo_3(15485867)). % 589,106 inferences, 0.085 CPU in 0.085 seconds (100% CPU, 6947773 Lips) true.
8.3. Generación de números primos
Ejercicio 3.1. Se considera el siguiente polinomio p(X)=X^2-X+41. Definir la
relación genera_primo(+X,+Y,-L)
que se verifica si L
es el conjunto de los
pares (A,p(A))
tales A
es un número del intervalo [X,Y]
y p(A)
es
primo. Por ejemplo,
?- genera_primo(5,7,L). L = [[5, 61], [6, 71], [7, 83]].
Solución
genera_primo(X,Y,L) :- findall([A,B],((between(X,Y,A), B is A^2-A+41, primo(B))),L).
Ejercicio 3.2. Comprobar que p(A)
es primo para A
desde 1 hasta 40, pero
no es primo para A=41.
Solución
?- genera_primo(1,40,_L), length(_L,40). true. ?- genera_primo(41,41,L). L = [].
8.4. Números amigos
Ejercicio 4.1. Definir la relación divisores_propios(+N,-L)
que se
verifique si L
es la lista ordenada de los divisores propios del
número N
. Por ejemplo,
?- divisores_propios(42,L). L = [1, 2, 3, 6, 7, 14, 21]
Solución
divisores_propios(N,L) :- N1 is N -1, findall(X,(between(1,N1,X), 0 =:= N mod X),L).
Ejercicio 4.2. Se dice que dos números X
e Y
son amigos cuando X
es igual
a la suma de los divisores de Y
(exceptuando al propio Y
) y viceversa. Por
ejemplo, 6 es amigo de sí mismo puesto que los divisores de 6 (exceptuando al 6)
son 1, 2 y 3 y 6=1+2+3. Igualmente 28 es amigo de sí mismo, puesto que 28=1+2+4+7+14.
Definir la relación amigos(+A,+B,-L)
que se verifica si L
es la lista de las
parejas de números amigos comprendidos entre A
y B4, con ~A
menor o igual que
B
. Por ejemplo,
?- amigos(1,2000,L). L = [6-6, 28-28, 220-284, 496-496, 1184-1210].
Solución
amigos(A,B,L) :- findall(X-Y, ((between(A,B,X), divisores_propios(X,Dx), sumlist(Dx,Y), between(X,B,Y), divisores_propios(Y,Dy), sumlist(Dy,X))), L).
8.5. Transformación de listas en conjuntos
Ejercicio 5. Definir la relación lista_a_conjunto(+L,-C)
que se verifica si
C
es el conjunto de los elementos de la lista no vacía L
. Por ejemplo,
?- lista_a_conjunto([b,c,d,a,c,f,a],C). C = [a, b, c, d, f]
Solución
lista_a_conjunto(L,C) :- setof(X,member(X,L),C).
8.6. Todos los elementos cumple una propiedad
Ejercicio 6. Definir la relación para_todos(+P,+L)
que se verifica si todos
los elementos de la lista L
cumplen la propiedad P
. Por ejemplo,
?- para_todos(number,[1,2,3]). true ?- para_todos(number,[1,2,3,a]). false.
Solución
para_todos(_,[]). para_todos(P,[X|L]) :- apply(P,[X]), para_todos(P,L).
8.7. Algún elemento cumple una propiedad
Ejercicio 7. Definir la relación existe(+P,+L)
que se verifica si existe
algún elemento de la lista L
que cumple la propiedad P
. Por ejemplo,
?- existe(number,[a,b,c]). false. ?- existe(number,[a,1,b,c]). true.
Solución
existe(P,[X|_]) :- apply(P,[X]), !. existe(P,[_|L]) :- existe(P,L).
8.8. Sublistas de elementos que verifican una propiedad
Ejercicio 8. Definir la relación sublista(+P,+L1,?L2)
que se verifica si L2
es la lista de los elementos de L1
que verifican la propiedad P
. Por
ejemplo,
?- sublista(number,[1,a,2,b,1],L). L = [1, 2, 1]
Nota: La relación predefinida correspondiente a sublista
es include
.
1ª solución
sublista(_P,[],[]). sublista(P,[X|L1],[X|L2]) :- Q =.. [P,X], Q, sublista(P,L1,L2). sublista(P,[X|L1],L2) :- Q =.. [P,X], \+ Q, sublista(P,L1,L2).
2ª solución
sublista_2(_P,[],[]). sublista_2(P,[X|L1],[X|L2]) :- Q =.. [P,X], Q, !, sublista_2(P,L1,L2). sublista_2(P,[_X|L1],L2) :- % Q =.. [P,_X], % \+ Q, sublista_2(P,L1,L2).
3ª solución
sublista_3(P,L1,L2) :- findall(X,(member(X,L1),apply(P,[X])),L2).
4ª solución
sublista_4(_,[],[]). sublista_4(P,[X|Xs],Ys) :- ( call(P, X) -> Ys = [X | Zs] ; Ys = Zs ), sublista_4(P, Xs, Zs).
8.9. Rotaciones de una lista
Ejercicio 9. Definir la relación rotaciones(L1,L2)
que se verifica si L2
es la lista cuyos elementos son las listas formadas por las sucesivas rotaciones
de los elementos de la lista L1
. Por ejemplo,
?- rotaciones([1,2,3,4],L). L = [[1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3]]. ?- rotaciones([2,3,4,1],L). L = [[2, 3, 4, 1], [3, 4, 1, 2], [4, 1, 2, 3], [1, 2, 3, 4]].
1ª solución
rotaciones(L1,L2) :- findall(L,es_rotacion(L1,L),L2). es_rotacion(L1,L2) :- append(L3,[X|L4],L1), append([X|L4],L3,L2).
2ª solución
rotaciones_2(L1,L2) :- findall(L, (append(L3,[X|L4],L1),append([X|L4],L3,L)), L2).
8.10. Lista de números capicúas en un intervalo
Ejercicio 10. Definir la relación capicúas(N,L)
que se verifica si L
es la
listas de los números enteros positivos capicúa menores que N
. Por ejemplo,
?- capicúas(100,L). L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...]. ?- set_prolog_flag(answer_write_options, [quoted(true), portray(true), max_depth(100)]). true. ?- capicúas(100,L). L = [1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99].
Solución
capicúas(N,L) :- setof(X,(between(1,N,X),es_capicúa(X)),L). es_capicúa(N) :- name(N,L), reverse(L,L).
8.11. Palabra inversa
Ejercicio 11. Definir la relación invierte_palabra(P1,P2)
que se verifica si
P2
es la palabra formada invirtiendo el orden de las letras de P1
. Por
ejemplo,
?- invierte_palabra(arroz,P). P = zorra.
Solución
invierte_palabra(P1,P2) :- name(P1,L1), reverse(L1,L2), name(P2,L2).
8.12. Determinación de un número por su factorial
Ejercicio 12. Definir la relación factorial_inverso(+X,-N)
que se
verifique si X
es el factorial de N
. Por ejemplo,
?- factorial_inverso(120,N). N = 5 ; false. ?- factorial_inverso(80,N). false.
1ª solución
factorial_inverso_1(X,N) :- factorial_inverso_1_aux(X,1,N).
factorial_inverso_1_aux(+X,+A,-N)
se verifica siN
es el menor número mayor o igual queA
cuyo factorial esX
.factorial_inverso_1_aux(X,A,A) :- factorial(A,X). factorial_inverso_1_aux(X,A,N) :- factorial(A,N1), N1 < X, A1 is A + 1, factorial_inverso_1_aux(X,A1,N).
factorial(+N,-X)
se verifica siX
es el factorial deN
.factorial(1,1). factorial(N,X) :- N > 1, N1 is N-1, factorial(N1,X1), X is X1 * N.
2ª solución (con memorización)
factorial_inverso_2(X,N) :- factorial_inverso_2_aux(X,1,N).
factorial_inverso_2_aux(+X,+A,-N)
se verifica siN
es el menor número mayor o igual queA
cuyo factorial (con memoria) esX
.factorial_inverso_2_aux(X,A,A) :- factorial_con_memoria(A,X). factorial_inverso_2_aux(X,A,N) :- factorial_con_memoria(A,N1), N1 < X, C1 is A + 1, factorial_inverso_2_aux(X,C1,N).
factorial_con_memoria(+N,-X)
se verifica siX
es el factorial deN
. Además almacena en la base de datos internas los factoriales calculados.:- dynamic factorial_con_memoria/2. factorial_con_memoria(1,1). factorial_con_memoria(N,X) :- N > 1, N1 is N-1, factorial_con_memoria(N1,X1), X is X1 * N, asserta(factorial_con_memoria(N,X) :- !).
3ª solución (con dos acumuladores)
factorial_inverso_3(X,N) :- factorial_inverso_aux_3(X,1,1,N).
factorial_inverso_aux_3(+X,+A,+F,-N)
se verifica siX = A*(A+1)*...*N
(de forma que siA = 1
entoncesX = N!
).factorial_inverso_aux_3(X,A,F,A) :- A*F =:= X. factorial_inverso_aux_3(X,A,F,N) :- F1 is A*F, F1 < X, !, A1 is A + 1, factorial_inverso_aux_3(X,A1,F1,N).
Comparación de eficiencia
La comparación es
?- factorial(2000,_X), time(factorial_inverso_1(_X,_N)). % 11,997,999 inferences, 2.921 CPU in 2.925 seconds (100% CPU, 4106828 Lips) true ?- factorial(2000,_X), time(factorial_inverso_2(_X,_N)). % 13,993 inferences, 0.004 CPU in 0.004 seconds (100% CPU, 3832958 Lips) true ?- factorial(2000,_X), time(factorial_inverso_3(_X,_N)). % 7,997 inferences, 0.009 CPU in 0.009 seconds (100% CPU, 877368 Lips) true ?- factorial(4000,_X), time(factorial_inverso_2(_X,_N)). % 35,993 inferences, 0.035 CPU in 0.035 seconds (100% CPU, 1035996 Lips) true ?- factorial(4000,_X), time(factorial_inverso_3(_X,_N)). % 15,997 inferences, 0.022 CPU in 0.022 seconds (100% CPU, 716325 Lips) true
8.13. Nodos de una generación en una lista de árboles binarios
Ejercicio 13. Un árbol binario es vacío o consta de tres partes: la raíz (que debe de ser un número positivo), el subárbol izquierdo (que debe ser un árbol binario) y el subárbol derecho (que debe ser un árbol binario). Usaremos la siguiente representación
nil
representa el árbol vacíot(I,R,D)
representa el árbol de la raízR
, subárbol izquierdoI
y subárbol derechoD
.
Por ejemplo, t(t(nil,2,nil),1,t(t(nil,4,nil),3,nil))
representa el árbol
1 / \ 2 3 / 4
Definir la relación generación(+N,+L1,-L2)
que se verifique si L2
es
la lista de nodos de la generación N
de la lista de árboles L1
. Por
ejemplo,
?- generación(0,[t(t(nil,2,nil),3,nil),t(nil,4,t(nil,5,nil))],L). L = [3, 4] ?- generación(1,[t(t(nil,2,nil),3,nil),t(nil,4,t(nil,5,nil))],L). L = [2, 5] ?- generación(2,[t(t(nil,2,nil),3,nil),t(nil,4,t(nil,5,nil))],L). L = []
Solución
generación(0,L,G):- findall(R,member(t(_,R,_),L),G). generación(N,L,G):- N > 0, elimina_raices(L,L1), N1 is N-1, generación(N1,L1,G).
elimina_raices(+L1,-L2)
se verifica siL2
es la lista de los árboles obtenidos de la lista de árbolesL1
eliminando sus raices. Por ejemplo,?- elimina_raices([t(t(nil,2,nil),3,nil),t(nil,4,t(nil,5,nil))],L). L = [t(nil, 2, nil), nil, nil, t(nil, 5, nil)]
Su definición es
elimina_raices([],[]). elimina_raices([nil|L1],L2):- elimina_raices(L1,L2). elimina_raices([t(I,_,D)|L1],[I,D|L2]):- elimina_raices(L1,L2).
8.14. Lista de elementos únicos
Ejercicio 14. Definir la relación únicos(+L1,-L2)
que se verifique
si L2
es la lista de los elementos que ocurren solamente una vez en la
lista L1
. Por ejemplo,
?- únicos([2,5,3,2],L). L = [5, 3] ?- únicos([2,5,5,2],L). L = []
Solución
únicos(L1,L2) :- findall(X,es_único(X,L1),L2).
es_único(?X,+L)
se verifica siX
es un elemento que ocurre solamente una vez en la listaL
. Por ejemplo,?- es_único(X,[2,5,3,2]). X = 5 ; X = 3 ; false.
Su definición es
es_único(X,L) :- select(X,L,R), not(memberchk(X,R)).
8.15. Elementos más frecuentes de una lista
Ejercicio 15. Definir el predicado populares(L1,L2)
que se verifique
si L2
es la lista de los elementos de L1
que aparecen el mayor
número de veces. Por ejemplo,
?- populares([rosa,juan,david,manu,rosa,nuria,david],L). L = [david,rosa]
Solución
populares(L1,L2) :- setof(X, ((member(X,L1), cuenta(X,L1,N1), not((member(Y,L1), cuenta(Y,L1,N2), N1 < N2)))), L2).
cuenta(+X,+L,-N)
se verifica siN
es el número de veces que aparece el elementoX
en la listaL
. Por ejemplo,?- cuenta(d,[r,j,d,m,r,n,d],N). N = 2
Su definición es
cuenta(_,[],0). cuenta(A,[B|L],N) :- A == B, !, cuenta(A,L,M), N is M+1. cuenta(A,[_B|L],N) :- % A \== _B, cuenta(A,L,N).
8.16. El problema 3n+1
Ejercicio 16.1. Consideremos la función siguiente definida sobre los números naturales: f(x) = 3x + 1, si x es impar; x / 2, si x es par
Definir la relación sucesión(+X,?L)
que se verifique si L
es la
lista de los elementos X, f(X), f(f(X)), ..., f^n(X)
tal que
f^n(X) = 1
. Por ejemplo,
?- sucesión(3,L). L = [3, 10, 5, 16, 8, 4, 2, 1]
Se dice que L
es la sucesión generada por X.
Solución
f(X,Y) :- X mod 2 =:= 0, !, Y is X/2. f(X,Y) :- % X mod 2 =/= 0, Y is 3*X+1. sucesión(1,[1]) :- !. sucesión(X,[X|L]) :- % X =/= 1, f(X,Y), sucesión(Y,L).
Ejercicio 16.2. Definir la relación longitudes(+X,?L)
que se
verifique si L
la lista de pares Y-N
donde Y
es un número de 1
a
X
y N
es la longitud de sucesión generada por Y
. Por ejemplo,
?- longitudes(5,L). L = [1-1, 2-2, 3-8, 4-3, 5-6]
1ª solución
longitudes(X,L) :- longitudes_aux(X,L1), reverse(L1,L). longitudes_aux(1,[1-N]) :- !, sucesión(1,L), length(L,N). longitudes_aux(X,[X-N|L]) :- % X > 1, sucesión(X,L1), length(L1,N), Y is X-1, longitudes_aux(Y,L).
2ª solución
longitudes_2(X,L) :- findall(Y-N,(between(1,X,Y),sucesión(Y,S),length(S,N)),L).
Ejercicio 16.3. Definir la relación longitud_máx(+X,?P)
que se
verifica si P
es un par de la forma Y-N4 donde ~Y
es un número entre
1
y X
tal que la longitud de la sucesión generada por Y
es la más
larga de las sucesiones generadas por 1, 2, ..., X
y N
es la
longitud de dicha sucesión. Por ejemplo,
?- longitud_máx(10,L). L = 9-20
Solución
longitud_máx(X,Y-N) :- longitudes(X,L), member(Y-N,L), \+ (member(_Z-M,L), M > N).
Ejercicio 16.4. Definir menor_que_~genera_mayor(+N,-M)~ que se
verifique si M
es el menor número natural tal que la longitud de la
sucesión generada por M
es mayor que N
. Por ejemplo,
?- menor_que_genera_mayor(100,N). N = 27.
Solución
menor_que_genera_mayor(N,M) :- menor_que_genera_mayor_aux(N,1,M). menor_que_genera_mayor_aux(N,M,M) :- sucesión(M,L), length(L,X), X > N, !. menor_que_genera_mayor_aux(N,X,M) :- Y is X+1, menor_que_genera_mayor_aux(N,Y,M).
8.17. Números perfectos
Ejercicio 17.1. Definir la relación suma_divisores_propios(+N,-S)
que se verifique si S
es la suma de los divisores propios del número
N
. Por ejemplo,
?- suma_divisores_propios(42,S).o S = 54. ?- suma_divisores_propios(1,S). S = 0.
Solución
suma_divisores_propios(N,S) :- divisores_propios(N,L), sum_list(L,S).
Ejercicio 17.2. Clasificamos los números naturales en tres tipos:
N
es de tipoa
siN
es mayor que la suma de sus divisores propiosN
es de tipob
siN
es igual que la suma de sus divisores propiosN
es de tipoc
siN
es menor que la suma de sus divisores propios
Definir la relación tipo(+N,-T)
que se verifique si T
es el tipo del
número N
. Por ejemplo,
?- tipo(10,T). T = a ?- tipo(28,T). T = b ?- tipo(12,T). T = c
1ª solución
tipo(N,T) :- suma_divisores_propios(N,S), tipo_aux(N,S,T). tipo_aux(N,S,a) :- N > S, !. tipo_aux(N,N,b) :- !. tipo_aux(_N,_S,c). % :- _N < _S.
2ª solución
tipo_2(N,T) :- suma_divisores_propios(N,S), ( N > S -> T = a ; N =:= S -> T = b ; true -> T = c).
Ejercicio 17.3. Definir la relación clasifica(+N,-L)
que se
verifique si L
es la lista de tipos de los números comprendidos entre
1
y N
. Por ejemplo,
?- clasifica(20,L). L = [a, a, a, a, a, b, a, a, a, a, a, c, a, a, a, a, a, c, a, c]
1ª solución
clasifica(N,L) :- findall(T,(between(1,N,X),tipo(X,T)),L).
2ª solución
clasifica_2(N,L) :- numlist(1,N,L1), maplist(tipo,L1,L).
Ejercicio 17.4. Definir la relación promedio(+N,-A,-B,-C)
que se
verifique si A
, B
y C
son las cantidades de números naturales
menores o iguales que N
de tipo a, b y c, respectivamente. Por
ejemplo,
?- promedio(20,A,B,C). A = 16, B = 1, C = 3.
Solución
promedio(N,A,B,C) :- clasifica(N,L), promedio_aux(L,A,B,C). promedio_aux([],0,0,0). promedio_aux([a|L],A1,B,C) :- promedio_aux(L,A,B,C), A1 is A+1. promedio_aux([b|L],A,B1,C) :- promedio_aux(L,A,B,C), B1 is B+1. promedio_aux([c|L],A,B,C1) :- promedio_aux(L,A,B,C), C1 is C+1.
Ejercicio 17.5. Definir la relación menor(+N,-X)
que se verifique si
X
es el menor número tal que la cantidad de números naturales menores o
iguales que X
de tipo a es N
. Por ejemplo,
?- menor(20,X). X = 25.
Solución
menor(N,X) :- menor_aux(N,N,X). menor_aux(N,M,M) :- promedio(M,N,_,_), !. menor_aux(N,M,X) :- M1 is M+1, menor_aux(N,M1,X).
8.18. Operación binaria aplicada a listas
Ejercicio 18. Definir la relación operación_listas(+O,+L1,+L2,-L3)
que se verifique si L3
es la lista obtenida aplicando la operación
binaria O
a los elementos de L1
y L2
que ocupan la misma posición
(Se supone que L1
y L2
tienen la misma longitud). Por ejemplo,
?- operación_lista(+,[1,2,3],[4,5,6],L). L = [5, 7, 9] ?- operación_lista(*,[1,2,3],[4,5,6],L). L = [4, 10, 18]
Solución
operación_lista(_,[],[],[]). operación_lista(O,[X1|L1],[X2|L2],[X3|L3]) :- E =.. [O,X1,X2], X3 is E, operación_lista(O,L1,L2,L3).
8.19. Eliminación de las vocales
Ejercicio 19. Definir la relación elimina_vocales(+P1,-P2)
que se
verifique si P2
es la palabra que se obtiene al eliminar todas las
vocales de la palabra P1
. Por ejemplo,
?- elimina_vocales(sevillano,P). P = svlln
1ª solución
elimina_vocales(P1,P2) :- name(P1,L1), códigos_vocales(L), findall(N,(member(N,L1),not(member(N,L))),L2), name(P2,L2).
códigos_vocales(?L)
se verifica siL
es la lista de los códigos ASCII de las vocales.códigos_vocales(L) :- name(aeiouAEIOU,L).
2ª solución
elimina_vocales_2(P1,P2) :- name(P1,L1), exclude(es_código_vocal,L1,L2), name(P2,L2).
es_código_vocal(X)
se verifica siX
es el códigos ASCII de una vocal.es_código_vocal(X) :- códigos_vocales(L), memberchk(X,L).
8.20. Palabras maximales
Ejercicio 20.1. Definir la relación longitud(+P,-N)
que se verifique
si N
es la longitud de la palabra P
. Por ejemplo,
?- longitud(ana,N). N = 3.
Solución
longitud(P,N) :- name(P,L), length(L,N).
Ejercicio 20.2. Definir la relación palabra_maximal(+L,-P)
que se
verifique si P
es una palabra maximal (es decir, de máxima longitud)
de la lista de palabras L
. Por ejemplo,
?- palabra_maximal([eva,y,ana,se,van],P). P = eva ; P = ana ; P = van.
Solución
palabra_maximal(L,P) :- select(P,L,R), longitud(P,N), not((member(P1,R), longitud(P1,N1), N < N1)).
Ejercicio 20.3. Definir la relación palabras_maximales(+L1,-L2
) que
se verifique si L2
es la lista de las palabras maximales de la lista
de palabras L1
. Por ejemplo,
?- palabras_maximales([eva,y,ana,se,van],L). L = [eva, ana, van].
Solución
palabras_maximales(L1,L2) :- findall(P,palabra_maximal(L1,P),L2).
8.21. Clausura transitiva
Ejercicio 21. La clausura transitiva de una relación binaria R
es la
menor relación transitiva que contiene a R
; por ejemplo, la clausura
transitiva de {(a,b),(b,c)}
es {(a,b),(b,c),(a,c)}
.
Definir la relación clausura_transitiva(R,X,Y)
que se verifique si
(X,Y)
está en la clausura transitiva de la relación R
. Por ejemplo,
suponiendo que se han definido las relaciones p
y q
por
p(a,b). p(b,c). q(c,b). q(b,a).
se tiene
?- clausura_transitiva(p,X,Y). X = a, Y = b ; X = b, Y = c ; X = a, Y = c ; false. ?- clausura_transitiva(q,X,Y). X = c, Y = b ; X = b, Y = a ; X = c, Y = a ; false.
Solución
clausura_transitiva(R,X,Y) :- apply(R,[X,Y]). clausura_transitiva(R,X,Y) :- apply(R,[X,Z]), clausura_transitiva(R,Z,Y).
8.22. Traducción de dígitos a palabras
Ejercicio 22. Definir la relación traducción(+L1,-L2)
que se
verifique si L2
es la lista de palabras correspondientes a los dígitos
de la lista L1
. Por ejemplo,
?- traducción([1,3],L). L = [uno,tres].
1ª solución
traducción_1([],[]). traducción_1([D|L1],[N|L2]) :- nombre(D,N), traducción_1(L1,L2).
nombre(D,N)
se verifica siN
es el nombre del dígitoD
.nombre(0,cero). nombre(1,uno). nombre(2,dos). nombre(3,tres). nombre(4,cuatro). nombre(5,cinco). nombre(6,seis). nombre(7,siete). nombre(8,ocho). nombre(9,nueve).
2ª solución
traducción_2(L1,L2) :- findall(N,(member(D,L1),nombre(D,N)),L2).
3ª solución
traducción_3(L1,L2) :- maplist(nombre,L1,L2).
8.23. Transformación de lista dependiente de la posición
Ejercicio 23. Definir la relación transforma(+L1,-L2)
que se
verifique si L2
es la lista obtenida sumándole a cada elemento
numérico de L1
su posición en la lista. Por ejemplo,
?- transforma([1,1,1,a,b,c,1,1,1],L). L = [2,3,4,a,b,c,8,9,10]. ?- transforma([1,2,a,5,2,b,3,1],L). L = [2,4,a,9,7,b,10,9].
1ª solución
transforma(L1,L2) :- transforma_aux(L1,1,L2).
transforma_aux(+L1,+N,-L2)
se verifica siL2
es la lista obtenida añadiéndole a cada elemento numérico deL1
la suma deN
y su posición en la lista.transforma_aux([],_,[]). transforma_aux([X|L1],N,[Y|L2]) :- number(X), !, Y is X+N, N1 is N+1, transforma_aux(L1,N1,L2). transforma_aux([X|L1],N,[X|L2]) :- % not(number(X)), N1 is N+1, transforma_aux(L1,N1,L2).
2ª solución
transforma_2(L1,L2) :- lista_de_posiciones(L1,L), maplist(suma,L1,L,L2).
lista_de_posiciones(+L1,-L2)
se verifica siL2
es la lista de posiciones correspondiente a la listaL1
. Por ejemplo,?- lista_de_posiciones([1,1,1,a,b,c,1,1,1],L). L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Su definición es
lista_de_posiciones(L1,L2) :- length(L1,N), numlist(1,N,L2).
suma(+X,+Y,-Z)
se verifica siZ
es la suma deX
y el númeroY
, cuandoX
es un número y es igual aX
, en caso contrario. Por ejemplo,?- suma(3,2,Z). Z = 5 ?- suma(b,2,Z). Z = b
Su definición es
suma(X,Y,Z) :- number(X), !, Z is X+Y. suma(X,_,X).
8.24. Aplanamiento de listas
Ejercicio 24. Definir la relación aplana(+L1,?L2)
que se verifique si
L2
es la lista obtenida reemplazando, recursivamente, cada lista de L1
por sus elementos. Por ejemplo,
?- aplana([a,[b,[c]],[[d],e]],L). L = [a, b, c, d, e]
Solución
aplana(X,[X]) :- \+ is_list(X). aplana([],[]). aplana([X|L1],L2) :- aplana(X,L3), aplana(L1,L4), append(L3,L4,L2).
9. Bibliografía
- J.A. Alonso. Introducción a la programación lógica con Prolog.
- I. Bratko.
Prolog programming for artificial intelligence (3 ed.)
(Addison–Wesley, 2001)
- Cap. 7: "More Built-in procedures"
- W.F. Clocksin y C.S. Mellish.
Programming in Prolog.
(Springer Verlag, 1994)
- Cap. 6: "Built-in predicates"
- T. Smith.
Artificial intelligence programming in Prolog.
(Univ. of Edinburgh, 2004).
- Cap. 14: Input/Output.
- T. Van Le.
Techniques of Prolog programming.
(John Wiley, 1993)
- Cap. 6: "Advanced programming techniques and data structures"