TEMA 3

Manipulación del CONTROL en PROLOG


 

1 Introducción

2 Corte (!) y fail

2.1 El Corte "!"

2.2 El predicado fail

2.3 Implementación de la negación y predicado repeat

3 Bibliografía


 

Introducción

En el capítulo anterior, hemos aprendido cómo es el esquema básico de trabajo en Prolog.

Sabemos que un programa lógico consta de una base de conocimientos donde expresamos los hechos y reglas de deducción que aportan la información 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 resolución, 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 función de si ha podido o no demostrar el objetivo según la base de conocimientos. Además proporciona también el conjunto de sustituciones o unificaciones para los parámetros de salida especificados en el objetivo.

El algoritmo de demostración del objetivo se basa en el uso de la técnica 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 ejecución lo lleva la máquina Prolog y, aparentemente, nosotros no podemos interferir en dicho control.

El hecho de que exista este tipo de control automático supone una extraordinaria ventaja a la hora de programar aunque, en ocasiones, también limita el funcionamiento o la eficiencia del programa diseñado. Para solventar esta limitación, se introduce en Prolog la posibilidad de incluir en nuestras bases de conocimientos unos predicados especiales que tienen como misión proporcionar una herramienta, un tanto artificial, para "controlar el control".

[ÍNDICE]

Corte (!) y fail

En este apartado describiremos el significado de los predicados "!" (Corte), y fail. Veremos gráficamente como actúan, y propondremos algunos ejemplos de cómo y cuándo deben utilizarse.

[ÍNDICE]

El Corte "!"

Podemos definir el Corte como un predicado que siempre se cumple, es decir, que genera un resultado verdadero en la primera ejecución, y falla en el proceso de backtracking, impidiendo dicho retroceso. Su aplicación principal es generar código más eficiente por el efecto que causa en la reducción o poda del árbol de búsqueda generado durante el procedimiento de resolución.

Para comprender el funcionamiento de este predicado nada mejor que considerar un par de ejemplos, cuyos árboles de búsqueda 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 ejecución se observa en la Figura 1.

 

Figura 1

Árbol de ejecución para la base de conocimientos y objetivo del ejemplo que no usa corte

Como se observa en el árbol de ejecución, cuando falla el segundo predicado del consecuente, al hacer backtracking, ignoramos la solución obtenida para el primer predicado en la rama anterior, y buscamos una nueva solución, con la esperanza de hallar aquella que, posteriormente, satisfaga al segundo predicado. Sin embargo, nosotros sabemos, a priori, que dicha solución 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 sería absurdo e ineficiente). Por tanto, no es necesario desarrollar la rama de retroceso representada por la línea discontinua en la Figura 1.

En la Figura 2 se muestra como queda el árbol al introducir en el código, el predicado corte. La rama que no se procesa se representa en color gris, para que sea posible la comparación 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 ejecución se observa en la Figura 2.

 

Figura 2

Árbol de ejecución utilizando el corte

[ÍNDICE]

Utilidad del predicado Corte

Las principales utilidades del Corte se exponen en los puntos siguientes:

[ÍNDICE]

El predicado fail

Se trata de un predicado que siempre falla, por tanto, implica la realización del proceso de retroceso para que se generen nuevas soluciones.

Una aplicación de este predicado, entre otras que ya se han comentado en el punto anterior, es la generación de todas las posibles soluciones para un problema.

Recordemos que cuando la máquina Prolog encuentra una solución para y devuelve el resultado de la ejecución. Con fail podemos forzar a que no pare y siga construyendo el árbol de búsqueda hasta que no queden más soluciones que mostrar. A continuación 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 ejecución se observa en la Figura 3 .

 

Figura 3

Ilustración del funcionamiento del predicado fail

[ÍNDICE]

Implementación de la negación y predicado repeat

Veamos cómo se implementan dos predicados interesantes: not y repeat

[ÍNDICE]

Predicado not (negación).

Como se comentó en el punto anterior, la negación se implementa como fallo, de modo que para negar algo, debemos demostrar que el objeto de la negación falla, es decir, para negar P, hemos de demostrar que P es falso y por tanto, la negación de P es verdadera.

El predicado not ya está implementado en Prolog pero conviene conocer dicha implementación.

not(P):-call(P), !, fail.

not(P).

Para comprender la implementación de la negación nos vamos a fijar en la Figura 4, que muestra el funcionamiento del predicado cuando P es cierto y en la Figura 5, que muestra el funcionamiento cuando P es falso.

Figura 4

Funcionamiento de not cuando P es cierto

Figura 5

Funcionamiento de not cuando P es falso

Como ya se ha comentado la interpretación dada de not se basa en una hipótesis de modelización que habitualmente se conoce con el nombre de "supuesto de mundo cerrado", y que se puede formular como la afirmación de que si no podemos demostrar que una cláusula es cierta, entonces podemos afirmar de forma coherente que la negación de la cláusula es verdadera. Sin embargo, esta no es la negación que se utiliza en la lógica clásica y, por tanto, introduce problemas en la actividad de modelización lógica [Adarraga, 1994].

Consideremos por ejemplo, la aserción "soltero(X) si no casado(X)". Dicha aserción parece equivalente a "casado(X) si no soltero(X)". Pero si utilizamos la primera aserción bajo el supuesto de mundo cerrado, con un individuo X de cuyo estado nada sabemos, podemos concluir que es soltero, mientras que si utilizamos la segunda aserción, podemos inferir que está casado.

casado(pepe).

soltero(X):-not(casado(X)).

Supongamos que no sabemos el estado de X=juan. Esto implica que casado(juan) falla y, por tanto, not(casado(juan)) es cierto, luego soltero(juan) es cierto.

soltero(pepe).

casado(X):-not(soltero(X)).

Supongamos que no sabemos el estado de X=juan. Esto implica que soltero(juan) falla y, por tanto, not(soltero(juan)) es cierto, luego casado(juan) es cierto.

Como vemos, el resultado del estado de un individuo del que no sabemos nada depende de cómo implementemos la base de conocimientos. Una situación, por tanto, ilógica y contradictoria.

[ÍNDICE]

Predicado repeat

Se implementa como:

repeat.

repeat:-repeat.

Esto implica que siempre que hacemos backtracking repeat es verdadero.

Ejemplo:

predicates

nondeterm repeat

opcion(integer)

nondeterm menu

clauses

repeat.

repeat:-repeat.

/* Cómo hacer un menú de opciones */

opcion(X):-X=0, write("Has elegido salir"), nl, !.

opcion(X):-write("No has elegido salir"), nl.

menu:-repeat,

write("Elige opción (0 para salir, Otro para continuar): "),

readint(X),

opcion(X),

X=0.

goal

menu.

 

La ejecución de la meta proporciona el siguiente resultado:

Elige opción (0 para salir, Otro para continuar): 1

No has elegido salir

Elige opción (0 para salir, Otro para continuar): 2

No has elegido salir

Elige opción (0 para salir, Otro para continuar): 3

No has elegido salir

Elige opción (0 para salir, Otro para continuar): 0

Has elegido salir

yes

[ÍNDICE]

Bibliografía

[Adarraga, 1994] Adarraga, Pablo. Zaccagnini José Luis. "Psicología e Inteligencia Artificial". Editorial Trotta. 1994.

[ÍNDICE]