Docsity
Docsity

Prepara tus exámenes
Prepara tus exámenes

Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity


Consigue puntos base para descargar
Consigue puntos base para descargar

Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium


Orientación Universidad
Orientación Universidad


Prolog, Apuntes de Ingeniería Aeronáutica

Asignatura: Introduccion a la inteligencia artificial, Profesor: , Carrera: Gestió Aeronàutica, Universidad: UAB

Tipo: Apuntes

Antes del 2010

Subido el 21/07/2008

garlak
garlak 🇪🇸

4.1

(40)

33 documentos

1 / 31

Toggle sidebar

Esta página no es visible en la vista previa

¡No te pierdas las partes importantes!

bg1
Programación Práctica en
Prolog
Área de Lenguajes y Sistemas Informáticos
Departamento de Informática
Octubre - 1998
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f

Vista previa parcial del texto

¡Descarga Prolog y más Apuntes en PDF de Ingeniería Aeronáutica solo en Docsity!

Programación Práctica en

Prolog

Área de Lenguajes y Sistemas Informáticos

Departamento de Informática

Octubre - 1998

Tabla de Contenidos

  • 1 Introducción _________________________________________________________
  • 2 Hechos ______________________________________________________________
  • 3 Reglas ______________________________________________________________
    • 3.1 Reglas simples ______________________________________________________
    • 3.2 Reglas con Variables _________________________________________________
    • 3.3 Reglas Recursivas ___________________________________________________
    • 3.4 Utilización de funciones _______________________________________________
    • 3.5 Datos Compuestos ___________________________________________________
  • 4 Unificación __________________________________________________________
  • 5 Listas _______________________________________________________________
    • 5.1 Definición _________________________________________________________
    • 5.2 Recorrer una lista ____________________________________________________
    • 5.3 Recorrer una lista tomando elementos de dos en dos___________________________
    • 5.4 Búsqueda de elementos en una lista _______________________________________
    • 5.5 Generación de una lista mediante concatenación de otras dos ____________________
    • 5.6 Descomposición de una lista en partes_____________________________________
    • 5.7 Generación de una lista filtrando elementos de otra lista ________________________
    • 5.8 Aplicación de un predicado a todos los elementos ____________________________
    • 5.9 Permutaciones de una lista _____________________________________________
  • 6 Aritmética ___________________________________________________________
  • 7 Aritmética con Listas _________________________________________________
    • 7.1 Obtener un valor a partir de una lista _____________________________________
    • 7.2 Acumulación de Resultados ___________________________________________
    • 7.3 Combinación miembro a miembro de los elementos de dos listas ________________
    • 7.4 Generación de una lista a partir de un valor ________________________________
    • 7.5 Generación de listas por filtrado de elementos ______________________________
    • 7.6 Clasificación de listas________________________________________________
    • 7.7 Creación de otra estructura a partir de una lista______________________________
    • 7.8 Otras Estructuras Recursivas___________________________________________
  • 8 Predicados Internos __________________________________________________
    • 8.1 Conversión de tipos _________________________________________________
    • 8.2 Chequeo de tipos ___________________________________________________
    • 8.3 Inspección de estructuras _____________________________________________
    • 8.4 Predicados meta-lógicos ______________________________________________
      • 8.4.1 Chequeo de tipo___________________________________________________________
      • 8.4.2 Comparación de términos no básicos ___________________________________________
      • 8.4.3 Conversión de Datos en Objetivos _____________________________________________
    • 8.5 Corte ____________________________________________________________
      • 8.5.1 Aplicaciones del Corte ______________________________________________________
    • 8.6 Predicados Extra-lógicos _____________________________________________
      • 8.6.1 Entrada/Salida ____________________________________________________________
      • 8.6.2 Acceso a la Base de Datos ___________________________________________________
    • 8.7 Predicados de Segundo Orden__________________________________________
  • 9 Ejercicios Propuestos _________________________________________________
  • 10 Bibliografía Comentada _______________________________________________
  • 11 Indice ______________________________________________________________

3.2 Reglas con Variables

madre(X,Y):-mujer(X), progenitor(X,Y).

mujer(pilar). mujer(belen). mujer(lucia). mujer(ana). mujer(maria). hombre(tomas). hombre(pedro). hombre(jose).

Equivale a: Para todo X e Y, si X es mujer y X es el progenitor de Y, entonce X es la madre de Y

4 En lógica de predicados: ∀x∀y(mujer(x)∧progenitor(x,y)→madre(x,y))

| ?- madre(belen,pedro). yes

| ?- madre(X,belen). X = pilar ;

no

| ?- madre(belen,X). X = ana ;

X = pedro

no

| ?- madre(X,Y). X = pilar , Y = belen ;

X = belen , Y = ana ;

X = belen , Y = pedro ;

no

La programación lógica basa su modelo en la utilización de relaciones, lo cual permite que un mismo procedimiento sirva para diferentes propósitos dependiendo de qué variables están instanciadas^2

En el ejemplo, una misma regla madre sirve para:

Comprobar si belen es madre de pedro. Calcular la madre de belén Calcular los hijos de belén Calcular parejas de madres/hijos

4 Obsérvese que las variables de los objetivos corresponden a cuantificadores existenciales, es decir:

?- madre(belen,X) equivale a:? ∃x (madre(belen,x))

Téngase en cuenta que a la hora de resolver la pregunta madre(belen,X) con la cabeza de la regla madre(X,Y) es necesario renombrar la variable X

El lenguaje Prolog realiza internamente un proceso de unificación que se describe en la página 4.

3.3 Reglas Recursivas

antepasado(X,Y):-progenitor(X,Y).

antepasado(X,Y):-progenitor(X,Z), antepasado(Z,Y).

En general, en una definición recursiva, es necesario considerar 2 casos: Caso básico : Momento en que se detiene la computación Caso Recursivo : Suponiendo que ya se ha solucionado un caso más simple, cómo descomponer el caso actual hasta llegar al caso simple.

4 Tanto el caso básico como el caso recursivo no tienen porqué ser únicos (puede haber varios casos básicos y varios casos recursivos)

| ?- antepasado(belen,X). X = ana ;

X = pedro ;

X = jose ;

X = maria ;

no

| ?- antepasado(X,belen). X = pilar ;

X = tomas ;

no

Las definiciones recursivas se resuelven de la misma forma que las reglas comunes. En la traza de este tipo de definiciones tiene especial importancia el renombramiento de variables.

& Considerando la relación progenitor como un enlace entre dos nodos de un grafo. La relación antepasado indicaría si hay camino entre dos nodos del grafo dirigido acíclico formado por la relación progenitor. Este tipo de relaciones se utiliza en diversos contextos como la búsqueda de caminos entre ciudades, la simulación de movimientos de un autómata, etc.

. Describir en Prolog una serie de caminos entre diversas ciudades y construir un predicado que indique

(^2) Se dice que una variable está instanciada si tiene un valor concreto (en realidad ya no sería una variable)

si 2 ciudades están conectadas.

3.4 Utilización de funciones

grande(pepe). grande(cabeza(juan)). grande(X):-mayor(X,Y).

mayor(cabeza(X),cabeza(Y)):- progenitor(X,Y).

Se utiliza la función: cabeza(x)=”cabeza de x” El programa indica: “Pepe es grande, la cabeza de juan es grande, si X es mayor que Y, entonces X es grande, además: La cabeza de X es mayor que la de Y si X es el progenitor de Y”

4 Prolog no necesita declaraciones de tipos. | ?- grande(X). X = pepe ;

X = cabeza(juan) ;

X = cabeza(pilar) ;

X = cabeza(tomas) ;

...

4 Las variables en Prolog no tienen tipo, de ahí que la respuesta X puede ser una persona ( pepe ) o una cabeza ( cabeza(juan) )

3.5 Datos Compuestos

horizontal(seg(punto(X,Y), punto(X1,Y))).

vertical(seg(punto(X,Y), punto(X,Y1))).

punto(X,Y) representa un punto de coordenadas (x,y) seg(p1,p2) representa un segmento cuyos extremos son los puntos p1 y p

Los argumentos de una función pueden ser funciones

| ?- horizontal(seg(punto(1,2),punto(3,2))). Yes

| ?- horizontal(seg(punto(1,2),P)). P = punto(_47796,2)

| ?- horizontal(P),vertical(P). P=seg(punto(_29128,_29130),punto(_29128,_29130) )

P = punto(_47796,2) indica que P es un punto cuya primera coordenada es una variable sin instanciar^3 y cuya segunda coordenada es 2

La última respuesta indica que para que un segmento sea vertical y horizontal a la vez, sus coordenadas deben ser las mismas (los números de las variables X e Y coinciden)

4 Unificación

Durante la resolución de objetivos, el sistema Prolog debe realizar la unificación entre los objetivos y las cabezas de las reglas o los hechos. De forma simplificada, el algoritmo de unificación consiste en: 1.- Inicializar σ = substitución vacía 2.- Si al aplicar σ a las 2 expresiones, éstas son iguales, finalizar y devolver σ 3.- Buscar de izquierda a derecha las primeras subexpresiones diferentes: Si dichas subexpresiones están formadas por una variable v y un término t (tal que v∉ t ) 4 Actualizar σ con el resultado de substituir v por t Volver a 2 En caso contrario Finalizar indicando que las expresiones no unifican | ?- f(X,X)=f(a,Y). X = a Y = a

| ?- f(X,X)=f(a,Y). no

| ?- p(f(X),g(Z,X))=p(Y,g(Y,a)). X = a , Z = f(a)

El operador ‘=’ se cumple si sus argumentos unifican. El operador ‘=’ se cumple si sus argumentos no unifican.

Puesto que Prolog no tiene chequeo de ocurrencias, se produce un error.

(^3) El formato de las variables sin instanciar varía de un sistema Prolog a otro. En estos apuntes, se utiliza un

número de 4 dígitos aleatorio precedido del carácter ‘_’ (^4) La condición v∉ t se conoce como chequeo de ocurrencias ( occur check ) y no es implementada por la mayoría

de los sistemas Prolog debido a su complejidad. Esto hace que en determinadas ocasiones, el sistema se meta en un bucle infinito tratando de unificar 2 términos

no

| ?- noPertenece(pilar,[jose,pilar,tomas]). no

| ?- noPertenece(luis,[jose,pilar,tomas]). yes

estuviese pusiese el caso recursivo antes, el sistema entraría en un bucle infinito.

De la misma forma, es conveniente que la llamada recursiva sea la última llamada de la definición recursiva (muchos sistemas optimizan las definiciones así escritas)

5.3 Recorrer una lista tomando elementos de dos en dos

todosIguales([]). todosIguales([X]). todosIguales([X,X|Xs]):-todosIguales([X|Xs]).

| ?- todosIguales([1,2,3]). no

| ?- todosIguales([1,1,1]). yes

| ?- todosIguales(X). X = [] ;

X = [_25576] ;

X = [_27704,_27704] ;

X = [_27704,_27704,_27704] ;

X = [_27704,_27704,_27704,_27704]

todosIguales(Xs):- Los elementos de Xs son todos iguales

Obsérvese que hay dos casos básicos y que el caso recursivo no es de la forma [X|Xs].

Obsérvese que los tres casos se excluyen. Como regla general, conviene que los casos de una definición se excluyan. Cuando los casos no se excluyen, al realizar backtracking el sistema puede obtener respuestas distintas de las esperadas.

. Describir la respuesta ante: ?- todosIguales([2,X,Y]).

5.4 Búsqueda de elementos en una lista

pertenece(X,[X|Xs]). pertenece(X,[Y|Ys]):- pertenece(X,Ys).

| ?- pertenece(pedro,[jose,pedro,tomas]). yes

| ?- pertenece(jose,[]). no

| ?- pertenece(X,[jose,pedro,tomas]). X = jose ;

X = pedro ;

X = tomas ;

no

| ?- pertenece(jose,X). X = [jose|_6617] ;

X = [_8808,jose|_8817] ;

X = [_8808,_11012,jose|_11021] ;

X = [_8808,_11012,_13230,jose|_13239] ...

pertenece(X,Xs) :- X está en la lista Xs

El caso básico no es la lista vacía. Además, los casos no se excluyen. Una lista que encaje con la primera definición, lo hará con la segunda.

La relación pertenece es un claro ejemplo de la flexibilidad de las definiciones en Prolog ya que se puede utilizar para:

1.- Chequear si un elemento pertenece a una lista

2.- Obtener todos los elementos de una lista por backtracking

3.- Obtener listas con un elemento X en primera, segunda, ... n-ésima posición.

elimina(X,[X|Xs],Xs). elimina(X,[Y|Ys],[Y|Zs]):-elimina(X,Ys,Zs).

| ?- elimina(1,[1,2,1,3],V). V = [2,1,3] ;

V = [1,2,3] ;

no

| ?- elimina(1,V,[2,3]).

eliminna(X,Ys,Zs):-Zs contiene todas las listas resultantes de eliminar el elemento X de Ys

4 El predicado selecciona sirve también para diversos propósitos: Borrar elementos de una lista Insertar un elemento en diferentes posiciones de una lista

V = [1,2,3] ;

V = [2,1,3] ;

V = [2,3,1] ;

no

. Describir el comportamiento del predicado si se añade X=Y en la segunda definición.

algunHombre(Xs):-pertenece(X,Xs), hombre(X).

| ?- algunHombre([jose,pilar,tomas]). yes

algunHombre(Xs):- alguno de los elementos X de Xs cumple la relación hombre(X)

5.5 Generación de una lista mediante concatenación de otras dos

concat([],Ys,Ys). concat([X|Xs],Ys,[X|Zs]):-concat(Xs,Ys,Zs).

| ?- concat([1,2],[3,4],V). V = [1,2,3,4]

| ?- concat([1,2],X,[1,2,3,4]). X = [3,4]

| ?- concat(X,Y,[1,2,3,4]). X = [] , Y = [1,2,3,4] ;

X = [1] , Y = [2,3,4] ;

X = [1,2] , Y = [3,4] ;

X = [1,2,3] , Y = [4] ;

X = [1,2,3,4] , Y = [] ;

no

concat(Xs,Ys,Zs):- Zs es el resultado de concatenar las listas Xs e Ys

4 Obsérvese la flexibilidad de la definición de concat que permite una gran variedad de usos dependiendo de qué variables están instanciadas en la llamada.

5.6 Descomposición de una lista en partes

Gracias a la flexibilidad de las relaciones lógicas, el predicado concat se utiliza en la definición de una gran cantidad de predicados.

prefijo(Xs,Ys):- concat(Xs,Bs,Ys). sufijo(Xs,Ys):- concat(As,Xs,Ys). sublista(Xs,Ys):- concat(AsXs,Bs,Ys), concat(As,Xs,AsXs).

| ?- prefijo([1,2],[1,2,3,4]). yes

| ?- sufijo([3,4],[1,2,3,4]). yes

| ?- pertenece1(1,[2,1,3]). yes

| ?- sublista([2,3],[1,2,3,4]). yes

Definir los predicados prefijo, sufijo y sublista de forma recursiva sin la ayuda del predicado concat

Definir mediante concat los predicados: pertenece(X,Xs):-X es un elemento de Xs reverse (Xs,Ys):-Ys es Xs con los elementos en orden inverso adyacentes(X,Y,Xs) :- X e Y están en posiciones consecutivas en Xs ultimo(Xs,X) :- X es el último elemento de Xs primeros(Xs,Ys) :-Ys es el resultado de eliminar el último elemento a Xs

La expresión E puede contener los operadores aritméticos clásicos (+, -, *, mod, etc.) y valores numéricos. Si contiene variables, éstas deben estar instanciadas a un valor numérico en el momento de la evaluación.

| ?- X is 3+5. X = 8

| ?- X is pepe. Error al evaluar

| ?- 8 is 3+5. yes

| ?- 4 is 3+5. no

| ?- X is 4/0. Error aritmético

| ?- X is X + 1. Error al evaluar

| ?- X = 3, Y is X + 5. X = 3 , Y = 8

| ?- X=0, X is X + 1. no

| ?- X = 3 + 5. X = 3 + 5

La evaluación de 3+5 se realiza internamente en una instrucción del procesador.

Si la expresión no resulta en un valor aritmético, se obtiene un error.

Al evaluar pueden producirse errores.

El operador “ is” no es nunca un operador de asignación como el := de Pascal. En general, una expresión del tipo X is X+1 no tiene sentido en Prolog.

Describir en qué situaciones “X is X + 1” es un error y en qué situaciones falla sin más.

4 El operador = unifica sus argumentos pero no evalúa.

par(X) :- 0 is X mod 2. impar(X) :- 1 is X mod 2. suma(X,Y,Z):- Z is X + Y.

| ?- par(3). no

| ?- par(4). yes

| ?- suma(2,3,V). V = 5

| ?- suma(2,V,5). Error al evaluar

Con el operador is se pierde la flexibilidad de las relaciones lógicas. Lo ideal es que al evaluar suma(2,V,5), el sistema devolviese V=

. ¿Por qué se obtiene error al evaluar suma(2,V,5) y no se obtiene error al evaluar par(4)?

Además del predicado is , los predicados de comparación realizan una evaluación aritmética de sus argumentos y se cumplen si los valores obtenidos cumplen las relaciones correspondientes:

| ?- 3+5 > 2+6. no

| ?- 3+5 >= 2+6. yes

| ?- 3+5 < 2+6. yes

| ?- 3+5 =< 2+6. yes

| ?- 3+5 =:= 2+6. yes

| ?- 3+5==2+6. no

& Para la comparación se utiliza el operador “=<” en lugar del más habitual “<=” debido a que éste último se asemeja a una doble flecha y se reserva para otros propósitos

. Describir la diferencia entre el comportamiento de los operadores: “=”, “is ” y “=:=”

fact(0,1). fact(N,F):-N > 0, N1 is N - 1, fact(N1,F1), F is N * F1.

fact(N,F):-F es el factorial de N

4 La versión de factorial aquí presentada es poco eficiente.

| ?- fact(5,V). V = 120

7 Aritmética con Listas

7.1 Obtener un valor a partir de una lista

sum([],0). sum([X|Xs],S):-sum(Xs,Sc), S is Sc + X.

long([],0). long([X|Xs],L):-long(Xs,Lc), L is Lc + 1.

prod([],1). prod([X|Xs],P):-prod(Xs,Pc), P is Pc * X.

| ?- sum([1,2,3,4],V). V = 10

| ?- long([1,2,3,4],V). V = 4

| ?- prod([1,2,3,4],V). V = 24

%% Version optimizada de sum sum1(Xs,S):-sumAux(Xs,0,S).

sumAux([],S,S). sumAux([X|Xs],Sa,S):-Sn is X + Sa, sumAux(Xs,Sn,S).

& Obsérvese la similitud entre las tres definiciones. En algunos lenguajes se utilizan construcciones de orden superior que permiten utilizar una única definición parametrizada por las operaciones y constantes^5.

4 Las definiciones ofrecidas no aprovechan la optimización de la recursividad de cola. Consiste en que el último objetivo de una definición recursiva sea el predicado que se está definiendo. Los sistemas con dicha optimización permiten que las definiciones recursivas se comporten de forma similar a un bucle en un lenguaje imperativo. sum1 es una versión optimizada de sum

4 La definición de sum1 sigue el patrón de acumulación de resultados que se ve a continuación

7.2 Acumulación de Resultados

A diferencia de los lenguajes imperativos, Prolog utiliza variables lógicas. En el momento en que una variable lógica es instanciada, dicho valor no puede modificarse. De esta forma, no es posible utilizar variables globales cuyo valor se modifique durante la resolución del objetivo. Existen ciertos algoritmos que requieren la utilización de un estado que almacena resultados intermedios. Para implementar dichos algoritmos es necesario utilizar un predicado auxiliar con un argumento extra que almacenará el estado que se modifica.

La definición de sum1 de la sección anterior sigue el patrón mencionado.

sumAcum(Xs,Ys):-sumAc(Xs,0,Ys).

sumAc([],S,[]). sumAc([X|Xs],Sa,[Sp|Ys]):-Sp is X + Sa, sumAc(Xs,Sp,Ys).

| ?- sumAcum([1,2,3,4],V). V = [1,3,6,10]

sumAcum(Xs,Ys):- (^) ∑

=

j

i

yj xi 1

para cada y (^) jYs

. Construir la definición del predicado maximo(Xs,M) que se cumple si M es el máximo de los elementos de Xs

7.3 Combinación miembro a miembro de los elementos de dos listas

prodEscalar(Xs,Ys,P):- pEsc(Xs,Ys,0,P).

pEsc([],[],P,P). pEsc([X|Xs],[Y|Ys],Pa,Pr):-Pn is Pa + X * Y, pEsc(Xs,Ys,Pn,Pr).

| ?- prodEscalar([1,2,3],[4,5,6],P). P = 32

prodEscalar(Xs,Ys,P):-P es el producto escalar de los vectores Xs e Ys (P = Σxiyi)

4 En esta definición se utiliza además el patrón de acumulación de resultados anterior.

7.4 Generación de una lista a partir de un valor

Es posible generar una lista mediante la descomposición de un valor. En el primer ejemplo, se descompone un número natural hasta llegar a cero, en el segundo, se descompone un intervalo hasta que los extremos son iguales.

(^5) En este sentido, los lenguajes funcionales se caracterizan por utilizar funciones de orden superior, permitiendo

una mayor reutilización de código.

ordenada([X,Y|Ys]):-X insertArbol(X,rama(X,A1,A2),rama(X,A1,A2)). insertArbol(X,rama(Y,A1,A2),rama(Y,A1n,A2)):- X < Y, insertArbol(X,A1,A1n). insertArbol(X,rama(Y,A1,A2),rama(Y,A1,A2n)):- X > Y, insertArbol(X,A2,A2n).

listArbol(Xs,A):-creaArbol(Xs,vacio,A).

creaArbol([],A,A). creaArbol([X|Xs],Ao,Ar):-insertArbol(X,Ao,An), creaArbol(Xs,An,Ar).

| ?- listArbol([2,1,3],V). V = rama(2,rama(1,vacio,vacio),rama(3,vacio,vacio))

busca(X,rama(X,,)). busca(X,rama(Y,A1,A2)):-X < Y, busca(X,A1). busca(X,rama(Y,A1,A2)):-X > Y, busca(X,A2).

nodos(vacio,[]). nodos(rama(X,A1,A2),Xs):-nodos(A1,Xs1), nodos(A2,Xs2), concat(Xs1,[X|Xs2],Xs).

ordenArbol(Xs,XsO):-listArbol(Xs,A), nodos(A,XsO).

| ?- ordenArbol([2,1,3,4],V). V = [1,2,3,4] ;

búsqueda resultante de insertar X en A

listaArbol(Xs,A):- A es el árbol de búsqueda creado a partir de la lista Xs

. Indicar qué patrón de recursividad se utiliza en la definición de listArbol

busca(X,A):- Se cumple si X está en el árbol A

nodos(A,Xs):- Xs son los nodos del árbol A.

& El predicado nodos realiza el recorrido del árbol empezando por los nodos de la izquierda, luego el nodo central y finalmente los de la derecha. Este recorrido se conoce como recorrido inorden. Existen otros recorridos como preorden y postorden

4 Obsérvese que con los árboles podrían definirse patrones recursivos similares a los definidos para las listas^9.

8 Predicados Internos

8.1 Conversión de tipos

En general, Prolog no contiene chequeo de tipos en tiempo de compilación, siendo necesario utilizar una serie de predicados predefinidos que chequean el tipo de sus argumentos.

Predicado Condición para que se cumpla Preguntas Simples atom_chars(A,Cs) Cs es la lista de caracteres que representa el átomo A

| ?- atom_chars(abc,V). V = [97,98,99]

| ?- atom_chars(V,"abc"). V = abc number_chars(N,Cs) Cs es la lista de caracteres que representa el número N

| ?- number_chars(123,V). V = [49,50,51]

| ?- number_chars(V,"123"). V = 123 number_atom(N,A) A es el átomo que representa el número N

| ?- number_atom(123,V). V = '123'

| ?- number_atom(V,'123'). V = 123

8.2 Chequeo de tipos

En general, Prolog no contiene chequeo de tipos en tiempo de compilación, siendo necesario utilizar una serie de predicados predefinidos que chequean el tipo de sus argumentos.

Predicado Condición para que se cumpla Ejemplos integer( X) X es un entero. 4

(^9) Actualmente, se investiga la posibilidad de utilizar lenguajes que definan automáticamente dichos patrones de

recursividad para los diferentes tipos de datos definidos por el usuario.

subTerL(S,[A|As]):- subTer(S,A). subTerL(S,[A|As]):- subTerL(S,As).

| ?- subTer(g(b),f(a,g(b),c)). yes

| ?- subTer(X,f(a,g(b))). X = f(a,g(b)) ;

X = g(b) ;

X = b ;

X = a ;

subTerm(T,T). subTerm(S,T):-compound(T), functor(T,F,A), subTermA(A,S,T).

subTermA(N,S,T):-arg(N,T,Ta), subTerm(S,Ta). subTermA(N,S,T):-N > 1, N1 is N - 1, subTermA(N1,S,T).

como ‘univ’

subTerm(S,T):- Se cumple si S es un subtérmino de T.

4 En general el predicado ‘=..’ proporciona la misma expresividad que los predicados ‘functor’ y ‘arg’ juntos. Sin embargo, aunque los programas con ‘=..’ son más legibles, también son menos eficientes, pues necesitan construir una lista auxiliar.

8.4 Predicados meta-lógicos

Los predicados meta-lógicos permiten controlar el algoritmo de resolución facilitando la meta-programación. Ésta consiste en construir programas que manipulan otros programas proporcionando una mayor expresividad al lenguaje.

8.4.1 Chequeo de tipo

Predicado Condición para que se cumpla Ejemplos var(X) X es una variable no instanciada | ?- var(X). X = _

| ?- X = 1, var(X). no

nonvar( X) X no es una variable o es una variable instanciada

| ?- nonvar(X). no | ?- X = 1, nonvar(X). X = 1

La utilización de estos predicados permite al programador chequear si una variable está instancia o no para proporcionar programas más flexibles y eficientes.

abuelo(X,Y):-nonvar(X), hombre(X), progenitor(X,Z), progenitor(Z,Y). abuelo(X,Y):-nonvar(Y), progenitor(Z,Y), progenitor(X,Z), hombre(X).

abuelo(X,Y) :- X es abuelo de Y

8.4.2 Comparación de términos no básicos

Predicado Condición para que se cumpla Ejemplos X==Y (^) X e Y son iguales (no unifica las variables)

| ?- f(X,2) = f(1,Y). X = 1 , Y = 2

| ?- f(X,2) == f(1,Y). no X == Y (^) X e Y no son iguales (no | ?- f(X,2) == f(1,Y). yes

Programación Práctica en Prolog Conversión de Datos en Objetivos

unifica las variables)

unifica(X,Y):-var(X),var(Y), X = Y. unifica(X,Y):-var(X), nonvar(Y), noOcurre(X,Y),X=Y. unifica(X,Y):-var(Y), nonvar(X), noOcurre(Y,X),Y=X. unifica(X,Y):-nonvar(X), nonvar(Y), atomic(X), atomic(Y), X = Y. unifica(X,Y):-nonvar(X), nonvar(Y), compound(X), compound(Y), unifTerm(X,Y).

unifTerm(X,Y):- functor(X,F,A), functor(Y,F,A), unifArgs(A,X,Y).

unifArgs(N,X,Y):- N > 0, unifArg(N,X,Y), N1 is N - 1, unifArgs(N1,X,Y). unifArgs(0,X,Y).

unifArg(N,X,Y):- arg(N,X,Ax), arg(N,Y,Ay), unifica(Ax,Ay).

noOcurre(X,Y):-var(Y), X == Y. noOcurre(X,Y):-nonvar(Y), atomic(Y). noOcurre(X,Y):-nonvar(Y), compound(Y), functor(Y,F,A), noOcurreArgs(A,X,Y).

noOcurreArgs(N,X,Y):- N > 0, arg(N,Y,An), noOcurre(X,An), N1 is N - 1, noOcurreArgs(N1,X,Y). noOcurreArgs(0,X,Y).

unifica(X,Y):- Se cumple si X e Y son unificables utilizando chequeo de ocurrencias.

| ?- unifica(f(1,X),f(Y,2)). X = 2 , Y = 1 ;

| ?- f(X,X)=f(Y,g(Y)). Error ... Stack Full, | ?- unifica(f(X,X),f(Y,g(Y))). no

noOcurre(X,T):- se cumple si la variable X no aparece en el término T

8.4.3 Conversión de Datos en Objetivos

El predicado call(X) se cumple si se cumple el objetivo X. o(X,Y):-call(X). o(X,Y):-call(Y).

| ?- o(progenitor(belen,tomas), progenitor(tomas,belen)). yes

| ?- progenitor(belen,tomas) ; progenitor(tomas,belen). yes

for(0,X). for(N,X):-call(X),N1 is N - 1, for(N1,X).

| ?- for(5,write('*')).

yes

| ?- T =.. [progenitor, tomas, belen], T. T = progenitor(tomas,belen) ;

o(X,Y):- se cumple si se cumple X o si se cumple Y

4 El predicado o(X,Y) está predefinido como el operador ‘;’

4 El Prolog Standard sustituye automáticamente una objetivo en forma de variable X por call(X)

Programación Práctica en Prolog Aplicaciones del Corte

ana y que ana no es un recién nacido, el Prolog poda la alternativa de que Pedro también es padre de María

4 La inclusión de cortes puede hacer que el sistema no encuentre algunas soluciones.

4 El exceso de cortes en un programa dificulta la legibilidad.

8.5.1 Aplicaciones del Corte

En general, la utilización del corte debe ser examinada con precaución. En muchas ocasiones conviene sustituir el corte por construcciones que encapsulen su utilización.

8.5.1.1 Indicar al Sistema que ha escogido la regla correcta (if-then-else)

soloMujeres([],[]). soloMujeres([X|Xs],[X|Ys]) :- mujer(X), soloMujeres(Xs,Ys). soloMujeres([X|Xs],Ys) :- hombre(X), soloMujeres(Xs,Ys).

| ?- soloMujeres3([ana,pedro,maria],V). V = [ana,maria] ;

no

soloMujeres(Xs,Ys):-Ys contiene las mujeres de Xs

Si se hace backtracking, el predicado chequeará más alternativas comprobando si las mujeres son, a su vez, hombres.

soloMujeres1([],[]). soloMujeres1([X|Xs],[X|Ys]) :- mujer(X), !, soloMujeres1(Xs,Ys). soloMujeres1([X|Xs],Ys) :- hombre(X), soloMujeres1(Xs,Ys).

soloMujeres1 : Si se inserta un corte tras la condición mujer se le indica al sistema que no busque más alternativas cuando encuentra una mujer.

soloMujeres2([],[]). soloMujeres2([X|Xs],[X|Ys]) :- mujer(X), !, soloMujeres2(Xs,Ys). soloMujeres2([X|Xs],Ys) :- soloMujeres2(Xs,Ys).

soloMujeres2: Utilizando el corte, si el sistema encuentra una mujer, no va a chequear si es un hombre. La condición de que sea un hombre, podría suprimirse.

ifThenElse(Cond,X,Y):-call(Cond)!,call(X). ifThenElse(Cond,X,Y):-call(Y).

soloMujeres3([],[]). soloMujeres3([X|Xs],Ys1) :- ifThenElse(mujer(X), Ys1 = [X|Ys], Ys1 = Ys), soloMujeres3(Xs,Ys).

El esquema conseguido puede generalizarse.

ifthenelse (Cond,X,Y) :-Si Cond entonces ejecuta X sino , ejecuta Y.

soloMujeres4([],[]). soloMujeres4([X|Xs],Ys1) :- (mujer(X) -> Ys1 = [X|Ys] ; Ys1 = Ys), soloMujeres4(Xs,Ys).

El predicado ifThenElse está predefinido mediante el operador ‘->’.

8.5.1.2 Negación por fallo

El lenguaje Prolog utilice un subconjunto de la lógica de predicados de primer orden (cláusulas Horn ) lo que impide modelizar ciertas situaciones con conocimiento negativo. La negación por fallo es una aproximación a las técnicas de representación de conocimiento negativo. La idea intuitiva es, si se detecta que algo no se cumple, entonces, se supone que es falso.

animal(X):-perro(X). animal(X):-serpiente(X).

serpiente(kika). perro(kiko).

Se desea representar el siguiente conocimiento:

“A Ana le gustan todos los animales salvo las serpientes”

fail es un predicado interno que siempre falla.

Programación Práctica en Prolog Aplicaciones del Corte

le_gusta(ana,X):-serpiente(X),!,fail. le_gusta(ana,X):-animal(X).

| ?- le_gusta(ana,kika). no

| ?- le_gusta(ana,kiko). yes

Se puede generalizar el patrón de negación por fallo.

falla_si(X):-call(X),!,fail. falla_si(X).

le_gusta2(ana,X):-falla_si(serpiente(X)), animal(X).

| ?- le_gusta2(ana,kika). no

| ?- le_gusta2(ana,kiko). yes

falla_si(X):- falla si el objetivo X se cumple y se cumple si X falla.

4 El predicado falla_si está predefinido en Prolog Standard como el operador: ‘+’.

& En muchos sistemas, el predicado ‘+’ se conoce como not. En la definición Standard se decidió evitar el predicado not para que los usuarios no lo confundiesen con una negación común. El símbolo ‘+’ pretende ser una representación del símbolo matemático? (no demostrable)

| ?- le_gusta2(ana,X). no

| ?- le_gusta2(ana,X). no

Con variables sin instanciar no funciona. En cuanto encuentra una serpiente, falla y no busca más alternativas.

le_gusta3(ana,X):- animal(X), falla_si(serpiente(X)).

En esta caso, puede corregirse cambiendo de orden los objetivos.

4 Al introducir negación por fallo el orden de los objetivos influye en los resultados.

& La raíz del problema es que la frase “A Ana le gustan todos los animales salvo las serpientes”, se formalizaría en lógica como

∀x (animal(x) ∧ ¬ serpiente(x) → le_gusta(ana,x) )

y al pasarla a forma clausal se obtendría una cláusula con dos literales positivos (no Horn )

8.5.1.3 Reglas por defecto

pension(X,invalidez):- invalido(X),!. pension(X,jubilacion):- jubilado(X),!. pension(X,nada). invalido(pedro). jubilado(tomas). jubilado(pilar).

| ?- pension(tomas,X). X = jubilado

| ?- pension(ana,X). X = nada

pension(P,Tp):- Tp es el tipo de pension que le corresponde a la persona P

J Con preguntas sencillas funciona

| ?- pension(tomas,nada). yes

| ?- pension(X,jubilacion). X = tomas

| ?-

K No funciona, debería responder que no!

L Tampoco funciona, le ha quitado la pensión a Pilar

4 La introducción de cortes puede hacer que al plantear al sistema preguntas no contempladas, las respuestas sean erróneas.

pension2(X,invalidez) :- invalido(X). pension2(X,jubilacion):- jubilado(X).

La solución sería quitar el corte y utilizar dos niveles.