Sei sulla pagina 1di 9

Introduccin

Sabemos que un programa lgico consta de una base de conocimientos donde expresamos los
hechos y reglas de deduccin que aportan la informacin completa acerca del mundo o dominio que
deseamos representar.
Por otro lado, disponemos de un motor de inferencia que aplica un algoritmo, concretamente el
algoritmo de resolucin, que permite inferir nuevos datos relativos al mundo que estamos
representando. Para ello, toma como entrada la base de conocimientos desarrollada y el objetivo
planteado y ofrece como salida un resultado de verdadero o falso en funcin de si ha podido o no
demostrar el objetivo segn la base de conocimientos. Adems proporciona tambin el conjunto de
sustituciones o unificaciones para los parmetros de salida especificados en el objetivo.
El algoritmo de demostracin del objetivo se basa en el uso de la tcnica de backtracking, de forma
que la inferencia de dicho objetivo se realiza a base de prueba y error. Debido a este tipo de
funcionamiento, podemos notar que el control de la ejecucin lo lleva la mquina Prolog y,
aparentemente, nosotros no podemos interferir en dicho control.
El hecho de que exista este tipo de control automtico supone una extraordinaria ventaja a la hora de
programar aunque, en ocasiones, tambin limita el funcionamiento o la eficiencia del programa
diseado. Para solventar esta limitacin, se introduce en Prolog la posibilidad de incluir en nuestras
bases de conocimientos unos predicados especiales que tienen como misin proporcionar una
herramienta, un tanto artificial, para "controlar el control".
1. Dando valor a las variables El mecanismo de unificacin
La unificacin es el mecanismo mediante el cual las variables lgicas toman valor en Prolog. El valor
que puede tomar una variable consiste en cualquier trmino, por ejemplo, j(3), 23.2, 'hola que tal', etc.
Por eso decimos que los datos que maneja Prolog son trminos.
Cuando una variable no tiene valor se dice que est libre. Pero una vez que se le asigna valor, ste
ya no cambia, por eso se dice que la variable est ligada.
Se dice que dos trminos unifican cuando existe una posible ligadura (asignacin de valor) de las
variables, tal que ambos trminos son idnticos sustituyendo las variables por dichos valores. Por
ejemplo: a(X,3) y a(4,Z) unifican dando valores a las variables: X vale 4, Z vale 3. Obsrvese que las
variables de ambos trminos entran en juego.
Por otra parte, no todas las variables estan obligadas a quedar ligadas. Por ejemplo: h(X) y
h(Y)unifican aunque las variables X e Y no quedan ligadas. No obstante , ambas
variablespermanecen unificadas entre s. Si posteriormente ligamos X al valor j(3) (por ejemplo),
entonces automticamente la variable Y tomar ese mismo valor. Lo que est ocurriendo es que, al
unificar los trminos dados, se impone la restriccin de que X e Y deben tomar el mismo valor aunque
en ese preciso instante no se conozca dicho valor.
La unificacin no debe confundirse con la asignacin de los lenguajes imperativos, puesto que
representa la igualdad lgica. Muchas veces unificamos variables con trminos directamente y de
manera explcita (ya veremos como se hace esto), por ejemplo, X y 355. Esto provoca la sensacin
de que estamos asignando valores a las variables al estilo imperativo.
Para saber si dos trminos unifican podemos aplicar las siguientes normas:
Una variable siempre unifica con un trmino, quedando sta ligada a dicho trmino.
Dos variables siempre unifican entre s. Adems, cuando una de ellas se liga a un trmino,
todas las que unifican se ligan a dicho trmino.
Para que dos trminos unifiquen, deben tener el mismo functor y la misma aridad. Despus se
comprueba que los argumentos unifican uno a uno manteniendo las ligaduras que se
produzcan en cada uno.
Si dos trminos no unifican, ninguna variable queda ligada.
Ejemplos
Una misma variable puede aparecer varias veces en los trminos a unificar. Ejemplo: k(Z,Z) y
k(4,H). Por culpa del primer argumento, Z se liga al valor 4. Por culpa del segundo argumento,
Z y H unifican, pero como Z se liga a un valor, entonces H se liga a ese mismo valor, que es 4.
Recuerde que una variable no puede ligarse a dos valores distintos. Por ejemplo: k(Z,Z) y
k(4,3) no unifican, sin embargo k(Z,Z) y k(5,5) s unifican.
Sera capaz de decir a que valores se ligan las variables de este ejemplo ? a(b(j,K),c(X)) y
a(b(W,c(X)),c(W)). Puede estar seguro de que unifican.
Cuidado con las variables annimas, recuerde que son todas distintas. Por ejemplo: k(_,_) y
k(3,4)unifican perfectamente.
2. Predicados y Objetivos
Los predicados son los elementos ejecutables en Prolog. En muchos sentidos se asemejan a los
procedimientos o funciones tpicos de los lenguajes imperativos.
Una llamada concreta a un predicado, con unos argumentos concretos, se denomina objetivo (en
ingls, goal). Todos los objetivos tiene un resultado de xito o fallo tras su ejecucin, indicando si el
predicado es cierto para los argumentos dados, o por el contrario, es falso.
Cuando un objetivo tiene xito, las variables libres que aparecen en los argumentos pueden quedar
ligadas. Estos son los valores que hacen cierto el predicado. Si el predicado falla, no ocurren
ligaduras en las variables libres.
Ejemplos
El caso ms bsico es aqul que no contiene variables: son_hermanos('Juan','Maria'). Este objetivo
slamente puede tener una solucin (verdadero o falso).
Si utilizamos una variable libre: son_hermanos('Juan',X), es posible que existan varios valores para
dicha variable que hacen cierto el objetivo. Por ejemplo para X = 'Maria', y para X = 'Luis'.
Tambin es posible tener varias variables libres: son_hermanos(Y,Z). En este caso obtenemos todas
las combinaciones de ligaduras para las variables que hacen cierto el objetivo. Por ejemplo, X = 'Juan'
y Z = 'Maria' es una solucin. X = 'Juan' y Z = 'Luis' es otra solucin.
3. Secuencias de objetivos
Hasta ahora hemos visto como ejecutar objetivos simples, pero esto no resulta demasiado til.
En Prolog los objetivos se pueden combinar mediante conectivas propias de la lgica de primer
orden: la conjuncin, la disyuncin y la negacin.
La disyuncin se utiliza muy poco y la negacin requiere todo un captulo para ser explicada. En
cambio, la conjuncin es la manera habitual de ejecutar secuencias de objetivos.
El operador de conjuncin es la coma; por ejemplo: edad(luis,Y),edad(juan,Z),X>Z. Parece sencillo,
pero hay que tener en cuenta qu ocurre con las ligaduras de las variables:
En primer lugar, hay que ser consciente de que los objetivos se ejecutan secuencialmente por
orden de escritura (es decir, de izquierda a derecha).
Si un objetivo falla, los siguientes objetivos ya no se ejecutan. Adems, la conjuncin, en total,
falla.
Si un objetivo tiene xito, algunas o todas sus variables quedan ligadas, y por tanto, dejan de
ser variables libres para el resto de objetivos en la secuencia.
Si todos los objetivos tienen xito, la conjuncin tiene xito y mantiene las ligaduras de los
objetivos que la componen.
Supongamos que la edad de Luis es 32 aos, y la edad de Juan es 25:
La ejecucin del primer objetivo tiene xito y liga la variable "Y", que antes estaba libre, al
valor 32.
Llega el momento de ejecutar el segundo objetivo. Su variable "Z" tambin estaba libre, pero
el objetivo tiene xito y liga dicha variable al valor 25.
Se ejecuta el tercer objetivo, pero sus variables ya no estn libres porque fueron ligadas en los
objetivos anteriores. Como el valor de "Y" es mayor que el de "Z", la comparacin tiene xito.
Como todos los objetivos han tenido xito, la conjuncin tiene xito, y deja las variables "Y" y
"Z" ligadas a los valores 32 y 25 respectivamente.
4. Varias soluciones
Hasta ahora todo parece sencillo, pero qu ocurre si uno o varios objetivos tienen varias
soluciones ?. Para entender cmo se ligan las variables, en este caso hemos de explicar en qu
consiste el backtracking en Prolog.
5. Backtracking
Supongamos que disponemos de dos predicados p/1 y q/1 que tienen varias soluciones (el orden es
significativo):
p(1) tiene xito.
p(2) tiene xito.
q(2) tiene xito.
No hay ms soluciones que stas.
Y a continuacin consideramos la siguiente secuencia: p(X),q(X).
Ahora ejecutamos la secuencia tal y como explicamos en la leccin anterior:
Ejecutamos p(X) con xito y la variable queda ligada al valor 1 (primera solucin).
Ejecutamos q(X), pero la variable ya no est libre, luego estamos ejecutando realmente q(1).
El predicado falla porque no es una de sus soluciones.
La conjuncin falla.
El resultado ha sido fallo, pero nosotros sabemos que para X = 2 existe una solucin para la
conjuncin.
Aqu es donde entra en juego el backtracking. Esto consiste en recordar los momentos de la
ejecucin donde un objetivo tena varias soluciones para posteriormente dar marcha atrs y seguir la
ejecucin utilizando otra solucin como alternativa.
El backtracking funciona de la siguiente manera:
Cuando se va ejecutar un objetivo, Prolog sabe de antemano cuntas soluciones alternativas
puede tener. En un prximo captulo veremos cmo puede llegar a saber esto. Cada una de
las alternativas se denomina punto de eleccin. Dichos puntos de eleccin se anotan
internamente y de forma ordenada. Para ser exactos, se introducen en una pila.
Se escoge el primer punto de eleccin y se ejecuta el objetivo eliminando el punto de
eleccin en el proceso.
Si el objetivo tiene xito, se contina con el siguiente objetivo aplicandole estas mismas
normas.
Si el objetivo falla, Prolog da marcha atrs, recorriendo los objetivos que anteriormente s
tuvieron xito (en orden inverso) y deshaciendo las ligaduras de sus variables. Es decir,
comienza el backtracking.
Cuando uno de esos objetivos tiene un punto de eleccin anotado, se detiene el
backtracking y se ejecuta de nuevo dicho objetivo usando la solucin alternativa. Las variables
se ligan a la nueva solucin y la ejecucin contina de nuevo hacia adelante. El punto de
eleccin se elimina en el proceso.
El proceso se repite mientras haya objetivos y puntos de eleccin anotados. De hecho, se
puede decir que un programa Prolog ha terminado su ejecucin cuando no le quedan puntos
de eleccin anotados ni objetivos por ejecutar en la secuencia.
Adems, los puntos de eleccin se mantienen aunque al final la conjuncin tenga xito. Esto permite
posteriormente conocer todas las soluciones posibles.
Ejemplo
La manera en que se ejecuta realmente nuestro ejemplo es la siguiente:
Prolog tiene que ejecutar p(X) y sabe (en el futuro veremos por qu), que existen dos
soluciones. En consecuencia, anota dos puntos de eleccin.
Ejecutamos p(X) usando el primer punto de eleccin, que se elimina en el proceso. Dicho
objetivo tiene xito y la variable queda ligada al valor 1 (primera solucin).
Hay que ejecutar q(X), que solamente tiene un punto de eleccin y queda anotado.
Ejecutamos q(X) eliminando su (nico) punto de eleccin, pero la variable ya no est libre,
luego estamos ejecutando realmente q(1). El predicado falla porque no es una de sus
soluciones.
Comienza el backtracking, recorriendo los objetivos en orden inverso hasta encontrar un punto
de eleccin anotado.
Nos topamos con el objetivo p(X). Se deshace la ligadura de la variable X, es decir, X vuelve a
estar libre.
Se encuentra un punto de eleccin. La ejecucin sigue de nuevo hacia adelante.
Ejecutamos de nuevo p(X), pero esta vez se usa el punto de eleccin que hemos encontrado.
Se liga la variable X al valor 2 que corresponde a la segunda solucin. El punto de eleccin se
elimina en el proceso.
Hay que ejecutar q(X), que slamente tiene un punto de eleccin y queda anotado.
Ejecutamos q(X) eliminando su (nico) punto de eleccin, pero la variable ya no esta libre,
luego estamos ejecutando realmente q(2). El objetivo tiene xito esta vez.
La conjuncin tiene xito manteniendo la ligadura de la variable X al valor 2.



6. Corte (!) y fail
En este apartado describiremos el significado de los predicados "!" (Corte), y fail. Veremos
grficamente como actan, y propondremos algunos ejemplos de cmo y cundo deben utilizarse.
6.1 El Corte "!"
Podemos definir el Corte como un predicado que siempre se cumple, es decir, que genera un
resultado verdadero en la primera ejecucin, y falla en el proceso de backtracking, impidiendo dicho
retroceso. Su aplicacin principal es generar cdigo ms eficiente por el efecto que causa en la
reduccin o poda del rbol de bsqueda generado durante el procedimiento de resolucin.
Para comprender el funcionamiento de este predicado nada mejor que considerar un par de ejemplos,
cuyos rboles de bsqueda se muestran en la Figura 1 y Figura 2.
Ejemplo:
Base de conocimientos sin utilizar el Corte.
padre(juan, pepe).
padre(juan, luis).
padre(juan, alberto).
hermanodepadre(X,Y):-padre(Z,X), padre(Z,Y).
Objetivo
?.- hermanodepadre(pepe, ana).
no
El proceso de ejecucin se observa en la Figura 1.
Figura 1. rbol de ejecucin para la base de conocimientos y objetivo del ejemplo que no usa
corte
Como se observa en el rbol de ejecucin, cuando falla el segundo predicado del consecuente, al
hacer backtracking, ignoramos la solucin obtenida para el primer predicado en la rama anterior, y
buscamos una nueva solucin, con la esperanza de hallar aquella que, posteriormente, satisfaga al
segundo predicado. Sin embargo, nosotros sabemos, a priori, que dicha solucin no va a ser posible,
porque ya hemos demostrado que juan es el padre de pepe y no lo es de ana, luego ambos no son
hermanos de padre, luego, en este caso, no interesa seguir buscando nuevos padres para pepe (esto
sera absurdo e ineficiente). Por tanto, no es necesario desarrollar la rama de retroceso representada
por la lnea discontinua en la Figura 1.
En la Figura 2 se muestra como queda el rbol al introducir en el cdigo, el predicado corte. La rama
que no se procesa se representa en color gris, para que sea posible la comparacin con el ejemplo
anterior.
Ejemplo:
Base de conocimientos utilizando el Corte.
padre(juan, pepe).
padre(juan, luis).
padre(juan, alberto).
hermanodepadre(X,Y):-padre(Z,X), !, padre(Z,Y).
Objetivo
?.- hermanodepadre(pepe, ana).
no
El proceso de ejecucin se observa en la Figura 2.
Figura 2 rbol de ejecucin utilizando el corte Utilidad del predicado Corte

Las principales utilidades del Corte se exponen en los puntos siguientes:
Confirmacin de la regla elegida. Por ejemplo, cuando calculamos el factorial, slo hay una
posible solucin para el mismo, sin embargo, desde el entorno de programacin, en la
ventana de objetivos, podemos pedir una nueva solucin. En lugar de buscarla, un programa
correcto debe indicar que no hay ms soluciones, aunque exista la posibilidad de realizar el
procedimiento de retroceso. La forma de construir dicho programa es mediante el uso
del Corte, es decir manejando desde la base de conocimientos el control de la
mquina Prolog.
Ahorrar comprobaciones innecesarias, como en el caso de los ejemplos vistos en el punto
anterior.
Parar al obtener una solucin y no permitir que se generen nuevas soluciones, aunque se lo
indiquemos de modo forzado.
Para construir la negacin conjuntamente con el predicado fail, como se ver en la seccin
"Implementacin de la negacin y predicado repeat".
6.2 El predicado fail
Se trata de un predicado que siempre falla, por tanto, implica la realizacin del proceso de
retroceso para que se generen nuevas soluciones. Una aplicacin de este predicado, entre otras
que ya se han comentado en el punto anterior, es la generacin de todas las posibles soluciones
para un problema.
Recordemos que cuando la mquina Prolog encuentra una solucin para y devuelve el resultado
de la ejecucin. Con fail podemos forzar a que no pare y siga construyendo el rbol de bsqueda
hasta que no queden ms soluciones que mostrar. A continuacin se ilustra el funcionamiento de
este predicado con un ejemplo sencillo.
Ejemplo:
Base de conocimientos.
padre(juan, pepe).
padre(juan, luis).
padre(juan, alberto).
listado:-padre(juan,X), write(X), nl, fail.
Objetivo
?.- listado.
pepe
luis
alberto
no.
El proceso de ejecucin se observa en la Figura 3 .


Figura 3
Ilustracin del funcionamiento del predicado fail
7. RECURSIVIDAD
La recursividad no es parte de ningn lenguaje, pero si puede ser implementada por todos. Es ms
un concepto el cual aboga por dividirse en tantas partes como sea posible para realizar el trabajo
pero siempre ejecutndose a si mismo.
Una funcin recursiva en otras palabras no es mas que aquella que se ejecuta ella misma una y otra
vez hasta que se cumpla una condicin determinada, resolviendo siempre una menor versin del
problema y usando su solucin para la siguiente resolucin donde en algn punto llega la solucin
final.

Pero an podemos utilizar Prolog en su mxima potencia si trabajamos con estructuras infinitas o
potencialmente infinitas, como podran ser las listas. El poder de los programas lgicos est en su
natural habilidad de manipular tipos de datos recursivos. Anteriormente vimos la definicin de lista.
Esta definicin es recursiva, es decir, podemos decir que una lista es o la lista vaca o un par donde la
segunda componente es una lista. Si hiciramos el pasaje a clusula de esta definicin, tendriamos
las siguientes reglas que nos definen una lista:
lista([]).
lista([X|Xs]):- lista(Xs).
Si tuviramos la consulta ?- lista([1,2,3], el rbol de prueba sera:

Notar que recin en la ltima instancia, es decir cuando consulta con lista([]), unifica con la primera
regla, en todas las consultas anteriores, siempre unifica con la segunda regla.
Podramos utilizar el predicado write(X) si quisiramos mostrar en pantalla los elementos de una lista,
modificando nuestro predicado lista de manera muy sencilla:
lista([]).
lista([X|Xs]):- write(X), lista(Xs).
Incluso, podemos, luego de la primera escritura, y para mayor prolijidad, hacer write( ) para dejar un
espacio entre cada elemento que se imprime.

Videos:
recursividad
http://www.youtube.com/watch?v=-GlpnUtnKIM
backtracking
http://www.youtube.com/watch?v=KVv_t28t1QI

Potrebbero piacerti anche