PROgramación LOGica PROLOG Dr. Oldemar Rodríguez Rojas Escuela de Informática Universidad de Nacional ¿Dónde bajar? Strawberry PROLOG: www.dobrev.com PROLOG -> PROgramming in LOGig Paradigma -> El lenguaje de la lógica puede se usado para programar Padre de PROLOG -> Francés Alain Colmerauer Fue desarrollado en 1970 en la Universidad de Marseilles En el curso usaremos Strawberry PROLOG Versión 2.3 La interfaz Ejemplo 1: le_gusta(elena,tenis). le_gusta(jony,football). le_gusta(tomas,baseball). le_gusta(erik,natacion). le_gusta(marco,tenis). le_gusta(_,tenis). le_gusta(juan,X) :- le_gusta(tomas,X). ?-le_gusta(juan,tenis). Las clausulas contienen hechos y reglas Un hecho es: le_gusta(elena,tenis) que corresponde a: "a elena le gusta el tenis" Una regla es: le_gusta(juan,X):-le_gusta(tomas,X). que corresponde a la regla lógica: a tomas le gusta X => a juan le gusta X o equivalentemente: a juan le gusta X si a tomas le gusta X Dos Corridas: ?-le_gusta(juan,baseball) Yes. ?-le_gusta(jony,tenis) Yes Variables: En la regla: le_gusta(juan,X) :- le_gusta(tomas,X). aparece la variable X La variables deben comenzar con letra mayúscula. Uso del fail Cuando PROLOG tiene una meta interna encuentra solamente una solución para cada submeta y continua con la próxima submeta, por lo que encuentra solamente una solución. Cuando PROLOG tiene una submeta a la derecha de una regla también encuentra solamente una solución. El uso del fail forzará el backtraking (reevaluación) en ambas situaciones con lo que sí se encontrarán todas las soluciones. EJEMPLO: Versión sin fail padre(leonardo,katherine). padre(carlos,jason). padre(carlos,marilyn). todos :padre(X,Y), write(X), write(" es el padre de "), write(Y),nl. ?-todos. Salida: leonardo es el padre de katherine Yes. EJEMPLO: Versión con fail padre(leonardo,katherine). padre(carlos,jason). padre(carlos,marilyn). todos :padre(X,Y), write(X), write(" es el padre de "), write(Y),nl,fail. ?-todos. Salida: leonardo es el padre de katherine carlos es el padre de jason carlos es el padre de marilyn No. Ejemplo de unificación de variables: ?-le_gusta(Persona,tenis),write(Persona). Salida: elena Yes ?-le_gusta(Persona,tenis),write(Persona), write(" "), fail. Salida: elena marco _0 juan No. Objetos y Relaciones Toda clausula en PROLOG está conformada por relaciones que afectan a objetos. Los nombres de las relaciones y de los objetos deben estar escritos en minúscula. Ejemplo 2: Metas (Goals) compuestas carro(toyota,130000,3,rojo,12000). carro(ford,90000,4,gris,25000). carro(nissan,8000,1,rojo,30000). ?-carro(M,K,E,C,P), P < 24000, write(M), write(" "), write(K), write(" "), write(E), write(" "), write(C), write(" "), write(P), write(" "). Salida: toyota 130000 3 rojo 12000 Yes. Observación: la , es el AND el ; es el OR P<25000 invoca en realidad <(P,25000) Variables Anónimas Para variables anónimas se utiliza "_", por ejemplo: ?-carro(_,_,Edad,_,Costo), Costo < 27000, write(Edad), write(" "), write(Costo), write(" "), fail. Salida: 3 12000 4 25000 No. Las variables anónimas pueden ser usadas en hechos, por ejemplo: le_gusta(_,tenis). se lee "a toda persona le gusta el tenis” El mecanismo"Backtraking" (reevaluación) Ejemplo 3: alumno(peter,9). alumno(paul,10). alumno(ana,9). alumno(susan,9). ?-alumno(P1,9), alumno(P2,9), not(P1=P2), write(P1), write(" "), write(P2),nl,fail. Este programa responde: peter ana peter susan ana peter ana susan susan peter susan ana No. El mecanismo"Backtraking" funciona como sigue: Ejemplo 4: Uso del Not Queremos escribir un programa que le busque novio a Sofía. El programa contiene una "base de datos" con la lista de solteros y sus características, además contiene una regla para los requisitos pedidos por Sofía, a saber, el novio debe ser vegetariano y no fumador. hombre(jose). hombre(bill). hombre(tom). fumador(jorge). fumador(tom). vegetariano(jose). vegetariano(tom). sofia_hace_pareja(X) :- hombre(X), not(fumador(X)), vegetariano(X). ?-sofia_hace_pareja(X), write("una posible pareja para Sofia es: "), write(X), nl. La salida al ejecutar este programa es: una posible pareja para Sofia es: jose No. hombre(jose). hombre(bill). hombre(tom). fumador(jorge). fumador(tom). vegetariano(jose). vegetariano(tom). sofia_hace_pareja(X) :- hombre(X), not(fumador(X)), vegetariano(X). ?-sofia_hace_pareja(X), write("una posible pareja para Sofia es: "), write(X), nl, fail. La salida al ejecutar este programa es: una posible pareja para Sofia es: jose No. hombre(jose). hombre(bill). hombre(tom). fumador(jorge). fumador(tom). vegetariano(jose). vegetariano(tom). sofia_hace_pareja(X) vegetariano(X). :- hombre(X), not(fumador(X)), ?-sofia_hace_pareja(X), write("una posible pareja para Sofia es: "), write(X), nl, fail. La salida al ejecutar este programa es: una posible pareja para Sofia es: jose Yes. Ejemplo 5: un árbol genealógico masculino(alan). masculino(carlos). masculino(luis). masculino(ivan). femenino(ana). femenino(andrea). femenino(eugenia). femenino(damaris). madre(eugenia,ana). madre(alan,damaris). padre(alan,luis). padre(ana,carlos). padre(andrea,luis). padre(eugenia,alan). padres(X,Y) :- madre(X,Y). padres(X,Y) :- padre(X,Y). hermano(X,Y) :masculino(Y) , padres(X,P) , padres(Y,P) , not(X=Y). /* /* /* /* /* El hermano of X es Y si */ Y es masculino y */ los padres de X son P y */ los padres de Y son P y */ X y Y no son el mismo */ hermana(X,Y) :femenino(Y) , padres(X,P) , padres(Y,P) , not(X=Y). /* /* /* /* /* La hermana de X es Y si */ Y es femenino y */ los padres de X son P y */ los padres de Y son P y */ X y Y no son el mismo */ tio(X,U) :madre(X,P) , hermano(P,U). /* El tio de X es U si */ /* la madre de X es P y */ /* el hermano de P es U. */ tio(X,U) :padre(X,P) , hermano(P,U). /* el tio de X es U si */ /* el padre de X es P y */ /* el hermano of P es U */ abuelo(X,G) :padre(P,G) , madre(X,P). */ /* El abuelo de X es G si */ /* si el padre de P es G */ /* y la madre de X es P. abuelo(X,G) :padre(X,P) , padre(P,G). /* El abuelo of X es G si */ /* el padre de X es P */ /* el padre de P es G */ ?-hermana(alan,X), write(X). La Salida es: andrea Yes. Simulación de compuertas lógicas en PROLOG Escriba en PROLOG un programa para simular (producir) la tabla de verdad del XOR usando OR, AND, NOT: cnot(1,0). cnot(0,1). cand(0,0,0). cand(0,1,0). cand(1,0,0). cand(1,1,1). cor(0,0,0). cor(0,1,1). cor(1,0,1). cor(1,1,1). cxor(X,Y,Z) :cnot(X,N1), cnot(Y,N2), cand(X,N2,N3), cand(Y,N1,N4), cor(N3,N4,Z). ?-cxor(I1,I2,S), write(I1), write(" "), write(I2), write(" "), write(S), write(" "), nl, fail. Una corrida puede ser: ?-cxor(I1,I2,S) 110 101 011 000 No. Variables Libres y Acotadas Se dice que una variable es LIBRE si PROLOG no conoce su valor. Se dice que una variable es ACOTADA si PROLOG conoce su valor. Ejemplo: le_gusta(elena,lectura). le_gusta(john,computadoras). le_gusta(john,ciclismo). le_gusta(leonardo,ciclismo). le_gusta(eric,natacion). le_gusta(eric,lectura). ?-le_gusta(X,lectura),le_gusta(X,natacion). Por ejemplo, en la meta: ?-le_gusta(X,lectura), le_gusta(X,natacion). en la submeta le_gusta(X,lectura) la variable X ocurre libre, mientras que en la submeta le_gusta(X,natacion) la variable X ocurre acotada. Pues cuando el mecanismo de backtraking inicia instancia la variable X con elena. Objetos Compuestos Un Objeto Compuesto consiste de un functor y los sub-objetos que lo conforman, esto es: functor(obj1,obj2,. . .,objn) Un objeto compuesto puede se vacio: functor() ó functor Ejemplo: estudiante es un objeto compuesto lee_estudiante(e(Nombre,Nota1,Nota2,Promedio,Resultado)) :- write("Cual es su nombre? "), read(Nombre), write("Nota en examen 1? "), read(Nota1), write("Nota en examen 2? "), read(Nota2), Promedio = (Nota1 + Nota2)/2, paso(Promedio,Resultado). paso(Promedio,Resultado) :- Promedio >= 70, Resultado = 'S'; Promedio < 70, Promedio >= 60, Resultado = 'A'; Promedio < 60, Resultado = 'P'. run:- lee_estudiante(E),nl,write(E),nl,nl, write("Desea continuar(s/n)"),read(Ch), =(Ch,'n'). run:-nl,nl,write("Digite los datos de otro estudiante"),nl,nl,run. ?-run. /* VER CORRIDA EJ8.SPJ */ Recursividad en PROLOG Ejemplo 1: u(1,2). u(N,Res) :- N > 1, N1 is N - 1, /* OJO dejar espacio después del - */ u(N1,Res1), Res is (2 * Res1). ?-u(4,X), write(X). Salida: 16Yes.. Ejemplo 2: fibonacci(1,1). fibonacci(2,1). fibonacci(N,F) :N > 2, M is N - 1, K is N - 2, fibonacci(M,G), fibonacci(K,H), F is G + H. ?-fibonacci(6,Res), write(Res). Listas en PROLOG Las Listas en PROLOG se manejan a través de Recursión. Las listas se encierran entre los operadores [ ] y se separan por comas. Ejemplos [1, 2, 8] [perro, gato, canario] ["Luisa", "Juan"] PROLOG divide una lista en dos partes: Head o cabeza (primer elemento de la lista) Tail o resto (una lista menos la cabeza) Ejemplos: Lista [1,2,3] [[1,2,3],[7,8],[]] [] [|] Head 1 [1,2,3] indefinido [] Tail [2,3] [[7,8],[]] indefinido [] NOTA: PROLOG usa | como separador entre el Head y el Tail Más ejemplos: Lista 1 [1,2,3,4,7,8] [7] [12,3,9,0] [1,2] [caballo|Q] Lista 2 [X|Y] [X|Y] [X,Y|Z] [3|X] [P|blanco] Instanciación X=1, Y=[2,3,4,7,8] X=7, Y=[] X=12, Y=3, Z=[9,0] falla P=caballo, Q=blanco Ejemplos de programas con listas Imprimir una lista imprime_lista([]). imprime_lista([H|T]) :- write(H), write(" "), imprime_lista(T). ?-imprime_lista([5,2,8,9,22]). Pertenece a una lista miembro(Nombre,[Nombre|_]). miembro(Nombre,[_|Tail]):miembro(Nombre,Tail). ?-miembro(2,[5,6,2,7,89,0]). Lectura de una Lista leerlista([X|Xr]):read(X),leerlista(Xr). leerlista([]). ?-leerlista(X),write(X). Un ejemplo completo append([],Lista,Lista). append([X|L1], L2, [X|L3]) :- append(L1,L2,L3). pares(X,[X|_]) :- Z is X mod 2, Z is 0. pares(X,[_|Cola]) :- pares(X,Cola). ultimo(X,[X]). ultimo(X,[_|Cola]) :- ultimo(X,Cola). consecutivos(X,Y,[X,Y|_]). consecutivos(X,Y,[_|Cola]) :- consecutivos(X,Y,Cola). invierte([],[]). invierte([H|T],L) :- invierte(T,Z), append(Z,[H],L). burbuja(L,O) :- append(X,[A,B|Y],L), B < A, append(X,[B,A|Y],M), burbuja(M,O), !. burbuja(L,L). imprime([]). imprime([Head|Tail]) :- write(Head),nl,imprime(Tail). leerlista([X|Xr]):- read(X),leerlista(Xr). leerlista([]). CORRIDAS: (ver ej14.spj) %?-leerlista(X),imprime(X). %?-append([4,5,2,3],[5,62,23,3,0],L),write(L). %?-pares(X,[3,4,6,9,7]),write(X). %?-ultimo(X,[3,4,6,9,7]),write(X). %?-consecutivos(6,9,[3,4,6,9,7]). %?-invierte([3,4,6,9,7],X),write(X). ?-burbuja([3,4,6,9,7],X),write(X). Más Ejemplos suma_lista([],0). suma_lista([CABEZA|COLA], RESULT) :suma_lista(COLA,R1), RESULT is CABEZA + R1. multiplica_lista([],1). multiplica_lista([CABEZA|COLA], RESULT) :multiplica_lista(COLA,R1), RESULT is CABEZA * R1. Más Ejemplos suma_dos_listas([],[],[]). suma_dos_listas([CABEZA1|COLA1], [CABEZA2|COLA2], [CABEZA3|COLA3]) :CABEZA3 is CABEZA1 + CABEZA2, suma_dos_listas(COLA1, COLA2, COLA3). producto_dos_listas([],[],[]). producto_dos_listas([CABEZA1|COLA1], [CABEZA2|COLA2], [CABEZA3|COLA3]) :CABEZA3 is CABEZA1 * CABEZA2, producto_dos_listas(COLA1, COLA2, COLA3). Ejemplos de sumatorias sumar(1,1) . sumar(N,Res) :- N1 is N - 1, sumar(N1,R1), Res is R1 + N. armonica(1,1) . armonica(N,Res) :- N1 is N - 1, armonica(N1,R1), Res is R1+1/N. %?-sumar(20,X),write(X). ?-armonica(20,X),write(X). El algoritmo de unificación usado por PROLOG Una variable libre puede ser unificada con cualquier término, luego la variable se considera acotada. Una constante puede ser unificada con si misma o con una variable libre. Un objeto compuesto puede ser unificado con un objeto compuesto, donde ambos tengan los mismos functores y el mismo número de argumentos, de modo que los términos son unificados dos a dos.