Top Banner
TUTORIAL DE PROLOG ( Ver 3.47) Índice INTRODUCCIÓN. CAPÍTULO 1. COMO CORRER PROLOG. 1.1 Comienzo. 1.2 Lectura de Archivos con Programas. 1.3 Ingreso de Cláusulas a la Terminal. 1.4 Directivas: Preguntas y Comandos. 1.5 Errores de Sintaxis. 1.6 Predicados no definidos. 1.7 Ejecución de Programas e Interrupción de éstos. 1.8 Salida del Intérprete. 1.9 Ejecuciones Anidadas - Break y Abort. 1.10 Guardando y Recuperando Estados de Programas. 1.11 Inicialización. 1.12 Entradas a Prolog. CAPÍTULO 2. DEPURACIÓN. 2.1 El Modelo de Flujo del Procedimiento Box Control. 2.2 Predicados Básicos de Depuración . 2.3 Rastreo. 2.4 Spy-Points. 2.5 Formato de los Mensajes de Depuración. 2.6 Opciones Disponibles Durante la Depuración. 2.7 Consultar Durante la Depuración.
28

Tutorial de prolog

Apr 14, 2017

Download

ivan
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Tutorial de prolog

TUTORIAL DE PROLOG ( Ver 3.47)

Índice

INTRODUCCIÓN.

CAPÍTULO 1. COMO CORRER PROLOG.

1.1 Comienzo. 1.2 Lectura de Archivos con Programas. 1.3 Ingreso de Cláusulas a la Terminal. 1.4 Directivas: Preguntas y Comandos. 1.5 Errores de Sintaxis. 1.6 Predicados no definidos. 1.7 Ejecución de Programas e Interrupción de éstos. 1.8 Salida del Intérprete. 1.9 Ejecuciones Anidadas - Break y Abort. 1.10 Guardando y Recuperando Estados de Programas. 1.11 Inicialización. 1.12 Entradas a Prolog.

CAPÍTULO 2. DEPURACIÓN.

2.1 El Modelo de Flujo del Procedimiento Box Control. 2.2 Predicados Básicos de Depuración . 2.3 Rastreo. 2.4 Spy-Points. 2.5 Formato de los Mensajes de Depuración. 2.6 Opciones Disponibles Durante la Depuración. 2.7 Consultar Durante la Depuración.

Page 2: Tutorial de prolog

CAPÍTULO 3. COMPILACIÓN.

3.1 Llamando al Compilador 3.2 Declaraciones Públicas. 3.3 Mezclando Código Compilado y Código Interpretado. 3.4 Declaraciones de Modo. 3.5 Indexación. 3.6 Optimización de la Cola de Recursión. 3.7 Limitaciones Prácticas.

CAPÍTULO 4. CONSTRUCCION DE PROCEDIMIENTOS.

4.1 Entradas / Salidas. 4.1.1 Lectura de Programas. 4.1.2 Manejo de Archivos.

4.1.2.1 Un Ejemplo. 4.1.3 Entrada y Salida de Términos. 4.1.4 Entrada / Salida de Caracteres.

4.2 Aritmética. 4.3 Comparación de Términos. 4.4 Ventajas. 4.5 Controles Extras. 4.6 Información Acerca del Estado de los Programas. 4.7 Meta-lógica. 4.8 Modificación de los Programas. 4.9 Base de Datos Interna. 4.10 Sets. 4.11 Compilando Programas. 4.12 Depurando. 4.13 Clausulas de Gramática. 4.14 Ambiente.

Page 3: Tutorial de prolog

INTRODUCCIÓN.

Prolog es un lenguaje de programación simple pero poderoso desarrollado en la Universidad de Marsella como una herramienta práctica para programación lógica. Desde el punto de vista del usuario, la ventaja principal es la facilidad para programar, ya que se pueden escribir rápidamente y con pocos errores, programas claramente leíbles.

Para una introducción al Prolog, se recomienda que se consulte el libro de Closkin y Mellish (81), sin embargo, para beneficio de aquellos que no tiene acceso al libro y para aquellos que tienen conocimientos anteriores de lógica, se encuentra un sumario del lenguaje en el apéndice I de esta obra.

Este manual describe el Sistema Prolog que fue desarrollado en el Departamento de Inteligencia Artificial de la Universidad de Edimburgo expresamente para el DECsystem-10. Este sistema se compone de un intérprete y un compilador, ambos escritos también en Prolog. A nivel del usuario, el compilador se puede considerar como un procedimiento integrado en el sistema que puede ser llamado por el intérprete.

Ya compilado, un procedimiento se puede correr a una velocidad de 10 a 20 veces más rápida, así como su almacenamiento también se ve reducido. Sin embargo, se recomienda que los usuarios nuevos ganen experiencia con el intérprete antes de intenten usar el compilador. El intérprete facilita el desarrollo y la prueba de programas así como también provee facilidades muy poderosas para la depuración del código.

Ciertos aspectos del Prolog no se han previsto en la instalación, por ejemplo el tipo de monitor en el que se va a usar, aspecto que lleva muchos de los procedimientos con los que el sistema cuenta. Este manual describe la instalación de la versión de Edimburgo bajo TOPS-10, versión 7.01, los usuarios de otro tipo de instalación se pueden referir al apéndice III.

Este manual se basa en la obra "User's Guide to DECsystem-10 Prolog" de L.M. Pereira, F.C.N. Pereira y D.H.D. Warren. Parte del capítulo 2 se tomo del libro de Byrd, (80). Comentarios muy útiles que se presentan en este manual fueron realizados por Lawrence Byrd, Luis Jenkins, Richard O'Keefe, Fernando Pereira, Robert Rae y León Sterling.

El sistema Prolog es mantenido por el Departamento de Inteligencia Artificial y el Engineering Board Computing Committee del Science and Engineering Research Council.

Page 4: Tutorial de prolog

CAPÍTULO 1. COMO CORRER PROLOG.

El Prolog para el DECsystem-10 ofrece al usuario un ambiente de programación con herramientas para construir programas, depurarlos siguiendo su ejecución a cada paso y modificar partes de los programas sin tener que volver a comenzar todo el proceso desde el último error.

El texto en un programa en Prolog se crea en un archivo o en un número de archivos usando cualquier editor de texto estándar. El intérprete de Prolog puede ser instruido para que lea los programas contenidos en estos archivos. A esta operación se le llama consultar el archivo.

1.1 Comienzo

Para correr el intérprete de Prolog, se deberá teclear el siguiente comando:

.r prolog

El intérprete responde con un mensaje de identificación y el indicador "| ?- " se presentará cuando el sistema esté listo para aceptar cualquier entrada, así, la pantalla se verá como sigue:

Edinburgh DEC-10 Prolog version 3.47

University of Edinburgh, September 1982

| ?-

En este momento el intérprete estará ya esperando que el usuario ingrese cualquier comando por ejemplo: una pregunta o una orden (ver sección 1.4). No se pueden introducir cláusulas inmediatamente (ver sección 1.3). Este estado se llama nivel alto del intérprete. Mientras que se teclea un comando, el indicador permanecerá de la siguiente manera:

"| ".

El indicador "?-" aparecerá solamente en la primera línea.

Page 5: Tutorial de prolog

1.2 Lectura de archivos con programas

Un programa está compuesto por una secuencia de cláusulas que se pueden intercalar con directivas para el intérprete. Las cláusulas de un procedimiento no necesariamente tienen que ser consecutivas, pero si es necesario recordar que el orden relativo es muy importante.

Para Ingresar un programa desde un archivo, sólo se tiene que teclear el nombre del archivo dentro de corchetes y seguidos de un ENTER:

| ?- [archivo] ENTER

Esta orden le dice al intérprete que tiene que leer (consultar) el programa. La especificación del archivo tiene que ser un átomo de Prolog, también puede contener especificación de dispositivos y/o una extensión, pero no se debe incluir la ruta de ubicación. Nótese que es necesario que todas estas especificaciones se coloquen dentro de comillas, por ejemplo:

| ?- ['dska:myfile.pl'].

Después de haber tecleado esto el archivo será leído,

Así las cláusulas que se han guardado en un archivo están listas para ser leídas e interpretadas, mientras que las directivas son ejecutadas al momento de ser leídas. Cuando se encuentra el fin del archivo, el intérprete pone en pantalla el tiempo que se ha gastado en la lectura y el número de palabras ocupadas por el programa. Cuando este mensaje se despliegue quiere decir que el comando se ha ejecutado con éxito.

También se pueden combinar varios archivos de la siguiente manera:

| ?- [miprograma,archivos_extras,archivos_de_tests].

En este caso los tres archivos serán leídos.

Si un nombre de archivo se antecede del signo "menos" (-), por ejemplo:

| ?- [-tests,-fixes].

Entonces este archivo o estos archivos serán releídos. La diferencia entre la lectura simple y la releída es que cuando un archivo es leído entonces todas las cláusulas en el archivo se añaden a la base de datos interna de Prolog, mientras que si se lee el archivo dos veces, entonces se tendrá una copia doble de todas las cláusulas.

Sin embargo, si un archivo es releído entonces las cláusulas de todos los procedimientos en el archivo reemplazarán a cualquier cláusula existente

Page 6: Tutorial de prolog

anteriormente. La reconsulta es útil para indicarle a Prolog las correcciones que se realizan a los programas.

1.3 Ingreso de las cláusulas a la terminal

Las cláusulas se puede ingresar directamente en la terminal pero esto es recomendado solamente cuando éstas no se necesitan permanentemente y cuando son pocas. Para ingresar cláusulas desde la terminal, se debe dar el comando especial: give the special command:

| ?- [user].

|

y el indicador "| " mostrara que el intérprete está ahora en el estado de espera de cláusulas o directivas. Para ingresar al nivel más alto del intérprete se deberá teclear Control + Z (^Z). Este es el equivalente al fin de archivo de archivo temporal virtual llamado ‘user’

1.4 Directivas: Preguntas y comandos

Las directivas pueden ser preguntas y/o comandos. Ambas pueden ser formas de dirigir al sistema para alcanzar alguna o algunas metas. En el siguiente ejemplo, la función de pertenencia a una lista se defina como sigue:

member(X,[X|_]).

member(X,[_|L]) :- member(X,L).

(Nótese que las variables sin valor asignado se representan como "_".)

La sintaxis completa para una pregunta es "?-" seguida por una secuencia de metas, por ejemplo:

?- member(b,[a,b,c]).

Page 7: Tutorial de prolog

Al nivel más alto del intérprete (en el que el indicador es como este: "| ?- "), una pregunta se puede teclear abreviando el "?-" que de hecho ya está incluido en el indicador. Así, en el nivel más alto, una pregunta se vería de la siguiente manera:

| ?- member(b,[a,b,c]).

Recuerde que los términos en Prolog deben terminar con el punto ".", por lo que el Prolog no ejecutará nada hasta que Usted haya tecleado este punto y un ENTER al final de la pregunta.

Si la meta que se ha especificado en la pregunta puede ser satisfecha, y si no hay variables a considerar como en este ejemplo, entonces el sistema responde:

yes

Y la ejecución de la pregunta ha terminado.

Si en la pregunta se incluyen variables, entonces el valor final de cada variable es desplegado (excepto para las variables anónimas). Así la pregunta:

| ?- member(X,[a,b,c]).

Podría ser respondida como:

X = a

En este momento el intérprete estará esperando que el usuario introduzca un ENTER o un punto "." Seguido de un ENTER. Si se teclea solamente ENTER, termina la pregunta y el sistema responde "yes". Sin embargo, si se teclea ".", el sistema realiza un backtracking y busca soluciones alternativas. Si no hay soluciones alternativas entonces el sistema responde:

no

La salida para algunas preguntas como la que se muestra abajo, donde un número precede a un "_" significa un nombre generado por el sistema para una variable, por ejemplo:

| ?- member(X,[tom,dick,harry]).

X = tom ;

X = dick ;

X = harry ;

Page 8: Tutorial de prolog

no

| ?- member(X,[a,b,f(Y,c)]), member(X,[f(b,Z),d]).

X = f(b,c),

Y = b,

Z = c

yes

| ?- member(X,[f(_),g]).

X = f(_52)

yes

| ?-

En el caso de los comandos, son similares a las preguntas excepto porque:

1. Las variables asociadas con un valor no se muestran cuando la ejecución del comando tiene éxito.

2. No existe la posibilidad de hacer backtracking para encontrar otras alternativas.

Los comandos inician con el símbolo ":-" . A nivel alto del intérprete simplemente basta con iniciar con el indicador "| ?- ". Cualquier salida pretendida debe ser programada explícitamente, por ejemplo, en el comando:

:- member(3,[1,2,3]), write(ok).

Dirige al sistema para que verifique si el número 3 pertenece a la lista [1,2,3], y a que redirija la salida hacia la variable "ok" si así es. La ejecución de un comando termina cuando todas las metas en el comando se han ejecutado de manera exitosa. No se muestran soluciones alternativas. Si no se encuentra solución entonces el sistema indica:

?

como una advertencia.

El uso principal de los comandos (en oposición al uso de las preguntas) es permitir a los archivos el contener las directivas que llaman a varios procedimientos, para los

Page 9: Tutorial de prolog

cuales el usuario no requiere salida impresa. En estos casos solo se requiere llamar a los procedimientos por su efecto, por ejemplo, si no quiere tener interacción con el intérprete durante la lectura de un archivo. Un ejemplo muy útil es el uso de una directiva que consulte una lista completa de archivos.

:- [ bits, bobs, main, tests, data, junk ].

Si un comando como este se encuentra contenido en el archivo 'myprog' entonces, teclear lo siguiente sería la forma más rápida para cargar el programa entero:

| ?- [myprog].

Cuando se interactúa al nivel más alto del intérprete de Prolog, la distinción entre comandos y preguntas es normalmente muy importante. A este nivel se teclean las preguntas en forma normal. Solo si se lee de un archivo, entonces para realizar varias metas se deben usar comandos, por ejemplo; una directiva en un archivo debe ser precedida por un ":-", de otra manera serían tratados como cláusulas.

1.5 Errores de sintaxis

Los errores de sintaxis se detectan durante la lectura. Cada cláusula o directiva, o en general cualquier término leído de un archivo o de la terminal, que provoca una falla, es desplegado en la terminal tan pronto como es leído por el sistema. Una marca indica el punto en la cadena de símbolos en donde al parser ha fallando en su análisis, por ejemplo:

member(X,X:L).

Implica que el parser muestre:

*** syntax error ***

member(X,X

*** here *** : L).

Si el operador "." No ha sido declarado como operado infijo.

Nótese que no se muestran comentarios que especifiquen el tipo de error. Si usted tiene duda acerca de qué cláusula contiene el error, puede usar el predicado:

listing/1

Page 10: Tutorial de prolog

Para enlistar todas las cláusulas que han sido eficientemente leídas, por ejemplo:

| ?- listing(member).

1.6 Predicados no definidos.

El sistema puede ocasionalmente también darse cuenta de que se han llamado a predicados que no tienen cláusulas, el estado de esta facilidad de identificación puede ser:

- 'trace', que causa que este tipo de errores sean desplegados en la terminal y que el proceso de depuración muestre la última vez que ha ocurrido.

O,

- 'fail', que causa que estos predicados fallen (este es el estado por default)

El predicado evaluable:

unknown(OldState,NewState)

unifica a OldState con el estado actual y coloca a State como Newstate. Este falla si los argumentos no son los apropiados. La depuración de predicados evaluables imprime el valor de este estado a lo largo de otras informaciones. Es importante notar que se lleva casi un 70% más de tiempo correr al intérprete en esta modalidad. Se espera que esta facilidad se pueda mejorar en el futuro.

1.7 Ejecución de programas e interrupción de éstos.

La ejecución de un programa se inicia cuando se le da al intérprete una directiva en la cual se contienen llamadas a uno de los procedimientos del programa.

Sólo cuando la ejecución de una directiva se ha realizado por completo es cuando el intérprete va por otra directiva. Sin embargo, uno puede interrumpir la ejecución normal de ésta al presionar ^C mientras el sistema está corriendo. Esta interrupción tiene el efecto de suspender la ejecución y se despliega el siguiente mensaje:

function (H for help):

Page 11: Tutorial de prolog

En este momento el intérprete acepta comando de una sola letra, que correspondan con ciertas acciones. Para ejecutar una acción simplemente se deberá teclear el carácter correspondiente (en minúsculas) seguido de un ENTER. Los posibles comandos son:

a aborta el comando actual tan pronto como sea posible

b detiene la ejecución (ver apartado 1.9)

c continuar la ejecución

e Salir de prolog y cerrar todos los archivos

g manejar posibilidad de recobrar aspectos ya borrados con anterioridad

h Lista todos los comandos disponibles

m Sale a nivel Monitorl (puede teclear Monitor o CONTINUE)

n desactiva el comando trace

t activa el comando trace

Nótese que no es posible romper la ejecución directamente desde el código compilado, cada una de las opciones "a", "b" y "t" piden que el intérprete realice una acción, por lo que la acción se seguirá realizando hasta que el código compilado devuelva en mando al intérprete.

Note también que la opción ("a") no saca al usuario cuando está en modo [user] es decir insertando cláusulas desde la terminal. Tiene que teclear ^Z para parar este proceso o teclear ("e") para poder salir de Prolog.

Si se trata de abortar un programa con ^C y accidentalmente se llegara al nivel Monitor, (quizá porque se apretó ^C demasiadas veces), deberá teclear CONTINUE para regresar a la interrupción ^C.

Page 12: Tutorial de prolog

1.8 Salida del intérprete

Para salir del intérprete y regresar al nivel monitor, deberá teclear ^Z a nivel alto del intérprete, o llamar al procedimiento "halt" (pre-cargado por default), o usar el comando "e" (exit) seguido de una interrupción ^C. ^Z es diferentes de los otros dos métodos de salida, ya que imprime algunas estadísticas. Es posible regresar usando el comando del Monitor CONTINUE después de ^Z, si se arrepiente de haber querido salir.

1.9 Ejecuciones anidadas, Break y Abort.

El sistema prolog integra una manera de suspender la ejecución de un programa e introducir nuevas directivas o cambiar metas a nivel alto del intérprete. Esto se logra si se llama a un predicado evaluable Break o tecleando "b" después de una interrupción con ^C ( ver sección 1.7).

Esto causa que el intérprete suspenda la ejecución inmediatamente, a lo que el intérprete contestará

[ Break (level 1) ]

Que quiere decir que se ha parado desde el nivel Break y que se pueden introducir preguntas de la misma manera en que se había hecho desde el nivel alto del intérprete.

El intérprete indica el nivel Break en el que el sistema se encuentra (por ejemplo, en Breaks anidados), imprimiendo el nivel de Break después de un yes o un no final a las preguntas. Por ejemplo, a nivel Break 2, se podría ver la respuesta así: uld look like:

| ?- true.

[2] yes | ?-

Un ^Z cierra la interrupción que se había realizado y se reanudan las tareas. Una ejecución suspendida se puede abortar usando la directiva:

| ?- abort.

Dentro de un break.

En este caso no se necesita un ^Z para cerrar el break; Todos los niveles break se descartarán y se llega automáticamente al nivel más alto del intérprete.

Page 13: Tutorial de prolog

1.10 Guardado y restauración de estados de programas

Una vez que el programa ha sido leído, el intérprete tendrá disponible toda la información que sea necesaria para su ejecución, esta información se llama estado de programa.

El estado de un programa se puede grabar en disco para ejecuciones posteriores. Para guardar un programa en un archivo, realice la siguiente directiva:

| ?- save(file).

Este predicado se puede llamar en cualquier momento, por ejemplo, puede ser muy útil a fin de guardar los estados de un programa inmediatos.

Cuando el programa ha sido guardado en un archivo, la siguiente directiva recuperaría el estado de un programa:

| ?- restore(file).

Después de esta ejecución el programa entonces podrá ser regresado exactamente al mismo punto en que se dejó al momento de salvarlo la ´´ultima vez.

| ?- save(myprog), write('programa restaurado').

Así, al ejecutarse esta orden con el mensaje propio del usuario, se obtendrá el mensaje "programa restaurado", por ejemplo.

Nótese que cuando una versión de Prolog nueva se instala, todos los programas grabados se vuelven obsoletos.

1.11 Inicialización

Cuando prolog inicia busca un archivo llamado 'prolog.bin' en la ruta default del usuario. Si se encuentra este archivo entonces se asume que el estado de un programa se ha restaurado. El efecto es el mismo que si se hubiera tecleado:

| ?- restore('prolog.bin').

Page 14: Tutorial de prolog

Si no se encuentra este archivo, entonces se busca en forma similar, el archivo 'prolog.ini'. Si se encuentra este archivo, sería lo mismo que haber tecleado:

| ?- ['prolog.ini'].

La idea de este archivo es que el usuario pueda tener aquí los procedimientos y directivas que se usan usualmente de manera que no se tengan que teclear cada vez que se necesiten.

El archivo 'prolog.bin' parece ser que es útil cuando se usa con el plsys(run(_,_)) que permite correr otros programas desde dentro de Prolog. Esta facilidad permite también regresar al mismo punto si se corrió prolog desde otros programas. En este caso, el predicado evaluable save/2 debe ser usado para salvar el estado del programa dentro de 'prolog.bin'.

1.12 Entrada a prolog

Cuando se entra a prolog directamente en una interacción con la terminal, se escribe automáticamente el archivo 'prolog.log' en modo append. Esto es, si ya existe el archivo, en el directorio del usuario, entonces la nueva información se añade, si no es así, y el archivo no existe, entonces el archivo se crea.

CAPÍTULO 2 DEPURACIÓN

Este capítulo describe las facilidades de depuración que están disponibles en el intérprete de Prolog, el propósito de estas facilidades es proveer información concerniente al control del flujo del programa, las principales facilidades del paquete de depuración son las siguientes:

- El Procedure_Box, modelo de ejecución de prolog que permite visualizar con fines de control el flujo del programa, especialmente durante un backtracking. El control de flujo se considera un nivel de procedimiento más que un procedimiento a nivel de cláusulas individuales.

- La habilidad para rastrear el programa o un grupo selectivo de puntos de este. Puntos espías que pueden ser procedimientos básicos o principales, en los que el programa deberá tener para que el usuario pueda interactuar con el sistema.

- La posibilidad de contar con opciones de control y de información, disponibles durante la depuración.

Page 15: Tutorial de prolog

2.1 El modelo de flujo del procedimiento Box Control

Durante la depuración de un programa se imprimen una secuencia de metas defectuosas a fin de mostrar el estado que el programa ha alcanzado durante esta ejecución. Sin embargo, para poder entender qué es lo que ocurre, es necesario entender primero cuándo y por que el intérprete imprime las metas. Como en otros lenguajes de programación, existen puntos principales de interés como los procedimientos de entrada y regreso, pero en Prolog, existe el backtracking que tiene una complejidad adicional. Una de las principales confusiones para los novatos es que los programadores tienen que enfrentar la cuestión de qué es lo que está ocurriendo en el momento en que una meta falla y el sistema inicia repentinamente el backtracking. El modelo Box de Prolog permite que el flujo del programa posibilite identificar cualquier punto en la ejecución en términos de localización en el texto del programa. Este modelo provee las bases para la depuración de un programa.

Examinando a fondo un procedimiento se tiene:

*--------------------------------------* Call | | Exit ---------> + descendant(X,Y) :- offspring(X,Y). + ---------> | | | descendant(X,Z) :- | <--------- + offspring(X,Y), descendant(Y,Z). + <--------- Fail | | Redo *--------------------------------------*

La primer cláusula establece que Y es descendiente de X si Y es resultado de X, y la segunda cláusula establece que Z es descendiente de X si Y lo es de X y si Z es descendiente de Y. En el diagrama, una caja se ha dibujada al rededor de todo el procedimiento y se han estipulado flechas para establecer el flujo de control con entradas y salidas de esta caja. Existen cuatro flechas por examinar durante el flujo:

• Call que representa la llamada inicial del procedimiento. Cuando una meta es de la forma descendant(X,Y) se requiere que el centro pase a través de la caja para ver un componente que haga matching y que de satisfacción a alguna de las submetas en el cuerpo de la cláusula. Este procedimiento es independiente del matching posible que se dé, por ejemplo, en la primera etapa se llama y el matching tiene lugar. Podemos imaginar textualmente el movimiento del código cuando se llega a una llamada de descendencia en alguna parte del código.

• Exit. Esta flecha representa un regreso exitoso del procedimiento. Este ocurre cuando la meta inicial ha sido unificada con uno de los componentes clausales y cualquiera de las submetas ha sido satisfecha. El control entonces pasa por el puerto de salida de la caja de descendencia. Textualmente se para el código siguiente por descendencia y se regresa por el camino por donde se llegó.

• Redo. Esta flecha indica que la meta subsiguiente ha fallado y que el sistema hace backtracking en un intento de encontrar alternativas a soluciones previas. El control pasa a través del puerto Redo de la caja de descendencia. Se hace un

Page 16: Tutorial de prolog

intento entonces por resatisfacer una de las submetas componentes en el cuerpo de la última cláusula en la que se ha tenido éxito, si esto falla, se hace un completo, nuevo, matching con la cláusula original al cuerpo de una nueva cláusula, textualmente se sigue la parte anterior de código de la manera en que se ha ido buscando nuevas formas de obtener éxito descomponiendo ésta en otras cláusulas y siguiendo así mientras que sea necesario.

• Fail. Esta flecha representa una falla en la meta inicial, que puede ocurrir si no existe un matching efectivo con alguna cláusula o si las submetas establecidas nunca son satisfechas, o si cualquiera de las soluciones producidas es rechazada siempre por el proceso posterior. El control pasa entonces por la caja de descendencia en el puerto Fail y el sistema continua en backtracking. Textualmente el sistema se mueve hacia atrás en el código que ha llamado este procedimiento y se continua moviéndose hacia atrás buscando puntos de elección.

En términos de este modelo, la información que nosotros obtenemos acerca de esta caja de procedimiento, es sólo acerca del flujo de control que se da a lo largo de estos cuatro puertos. Esto significa que a este nivel no importa con cual cláusula se hace matching y como se da satisfacción a las submetas, sino más bien como quisiéramos conocer la meta inicial y la final tienen efecto.

Note que la caja que se ha dibujado para cercar el procedimiento tiene que ser vista como una caja de invocación, esto es, que hay una caja diferente para cada procedimiento, obviamente con algunos procesos recurrentes en todos los procedimiento, con diferentes llamadas y salidas en el flujo de control, pero estas para diferentes invocaciones. Por ello se les dé una entero único de identificación a cada una de estas cajas.

2.2 Predicados básicos de depuración.

El intérprete proporciona un rango de predicados evaluables para el control de las facilidades de evaluación, lo más básicos son los siguientes:

• debug que coloca al modo Debug en On (usualmente está en off) a fin de tener un rango amplio de control en el flujo de información disponible desde el principio. Cuando el debug está en off, el sistema no recuerda los llamados hechos a procedimientos anteriormente, sino que solamente recuerda las invocaciones que se están ejecutando. Se puede llamar a medias de una ejecución después de haber dado un ^C ( ver el comando trace) pero la información anterior no estará disponible.

• nodebug Pone a debug en off, y si existen puntos espías, éstos serán borrados. • state. Muestra el estado de debug.

Page 17: Tutorial de prolog

2.3 Rastreo

El siguiente comando evaluable puede ser usado a fin de tener un control más efectivo del programa que se va a depurar.

• trace coloca el switch debug en on, si es que no se ha hecho ya, y asegura que el siguiente tiempo de control, se desplegará un mensaje y se le pedirá al usuario que interactue con el sistema. El efecto del rastreo también puede ser logrados tecleando un "t" después de una interrupción con ^C.

• leash(Mode) coloca el Leashing_Mode en forma de modo, donde modo puede tener cualquiera de los siguientes valores:

full - prompt on Call, Exit, Redo y Fail tight - prompt on Call, Redo y Fail half - prompt on Call y Redo loose - prompt y Call off - never prompt

o cualquiera otra combinación de estos y de los puertos descritos en la sección 4.12

El valor inicial del modo es "half", se puede cambiar si se modifica el archivo "prolog.ini".

2.4 Spy-points

Para programas extensos o de cualquier extensión es prácticamente imposible que el programador siga paso a paso todo lo que está ocurriendo en cada módulo o en cada meta o submeta. Es por ello que se posibilita la tarea de formular puntos espías que permiten mantener un control exacto solamente en puntos clave elegidos por el usuario para el control total del programa y su curso.

La selección del modo que se realice no tiene injerencia alguna en los puntos espías que se han elegido por parte del usuario.

Los puntos espías son conjuntos de establecimientos de variables y de predicados evaluables o también de operadores estándares.

Page 18: Tutorial de prolog

spy X Coloca en On el modo de los puntos espías, se puede usar en cualquier predicado o punto del programa en una oración evaluable o en una directiva establecida previamente.

nospy X Funciona de manera similar a spy X pero en esta caso x es un conjunto de puntos espías definidos previamente y removidos.

2.5 Formato de los mensaje de depuración.

Se puede ver el formato que tienen exactamente y a profundidad los mensajes de error, esto permitirá realizar de una manera más eficiente las tareas de depuración de programas al posibilitar el conocimiento de los símbolos usados en cada mensaje como este

** (23) 6 Call : foo(hello,there,_123) ?

"**" indica que es un spy-point. Si este punto no es un conjunto de puntos espías predefinidos entonces el puntero se convierte en un ">". Que da las siguientes posibles combinaciones:

"**" un spy-point.

"*>" un spy point con un skip la última vez que se estuvo en la caja

" >" No es un sapy point pero si hubo un skip la última vez que se estuvo en la caja

" " No es un spy point.

Nótese que en esta caso las acciones son rastreadas a fin de mantener un centro exacto en el depurado durante el establecimiento de este tipo de mensajes.

2.6 Opciones disponibles durante la depuración

Esta sección describe las opciones particulares que están disponibles cuando el sistema te muestra después de algún mensaje de depuración. Todas las opciones son una letra como nemónico, y algunas pueden ser opcionales seguidas de un entero decimal. Algunas opciones solo requieren el terminador.

Page 19: Tutorial de prolog

La única opción que se hace necesaria de recordar es "h" y un ENTER, ya que es la función que desplegará a las siguientes funciones:

<cr> creep c creep

<lf> leap l leap

<esc> skip s skip

x back to choice point q quasi skip

r retry r <n> retry goal n f fail f <n> fail goal n

; redo

a abort e salir de Prolog

h help p imprime una meta

w escribe meta d despliega una meta

g imprime ancestros g <n> ultimo n ancestros

@ comando accept b break

[ consult user n nodebug

Las primeras tres opciones son decisiones de control básicas ya que son las más frecuentemente usadas, cada una puede ser llamada con una sola tecla c <cr> Creep

Causa que el intérprete se vaya de un solo paso al siguiente puerto e imprima un mensaje. Si el puerto está atascado se le pedirá al usuario acciones posteriores, de otra manera el procedimiento continuará hasta terminar en la terminal.

l <lf> Leap

Causa que el intérprete regrese ala ejecución del programa, solo para cuando un punto espía se ha encontrado, o cuando el programa termina

s <esc> Skip

Es válido solamente en el puerto Redo, brinca sobre una ejecución entera o un procedimiento. El uso de esta directiva de compilación garantiza que el control será devuelto a la caja de control después de la ejecución dentro de la caja. Cuando se da

Page 20: Tutorial de prolog

un cuasibrinco o cuasiskip en donde no se ignoran los puntos espías, la opción "t" después de un ^C deshabilitará las máscaras.

q Quasi-skip

Es como skip, excepto porque este brinco no oculta a los puntos espías para la depuración. Si existe un punto espía en la ejecución de una meta, entonces el control regresa al punto en donde la acción se debe realizar.

x Back to choice point

Da la posibilidad de hacer un fail rápido, llevando al programa al punto real de decisión anterior. Cuando el usuario sabe que algo hará fail, puede anticipar el punto de decisión y verificarlo rápidamente sin tener que repetir el proceso completo. Si se da una secuencia de Fails seguidos de una secuencia de Redos (o si la secuencia está vacía) se tienen mensajes estándares (como los mostrados anteriormente) excepto para los primeros dos caracteres donde será "=>". Cuando se llama a la acción exit, la operación de salida ocurrirá normalmente.

r Retry

Se puede usar en cualquiera de los cuatro puertos, aunque el puerto call no tenga efecto en realidad. Esto permite restablecer la invocación cuando por ejemplo, el usuario se ha dado cuenta de que ha obtenido resultados algo raros. El estado de la ejecución es exactamente el mismo que el alcanzado durante la ejecución normal original. Si se proporciona un entero con el comando retry entonces se tomará como una especificación de que el sistema trate de invocar al puerto y no a la caja actual, por supuesto que a un puerto designado por el usuario. Desafortunadamente el proceso no puede ser garantizado. Se puede suplir con cuts (!) al interior del programa. Un mensaje de error "[ ** JUMP ** ]" se desplegará en la terminal para indicar que es lo que ha ocurrido.

f Fail

es similar al retry ya que transfiere el control al puerto fail desde la caja actual, en una posición en donde se está cercado a hacer un backtracking

; Redo

Puede ser llamado en una salida y forzar a mover al puerto Redo

a Abort

Causa que se aborte la ejecución actual. Todas las ejecuciones se mantienen hasta que el usuario se asegura de que no las necesita conforme sube al mas alto nivel del intérprete (es equivalente al predicado evaluable abort)

Page 21: Tutorial de prolog

e Exit from Prolog

Causa una salida irreversible del sistema de prolog, de regreso al Monitor (esta directiva es equivalente a Halt)

h Help

Despliega la tabla de opciones marcada arriba

p Print goal

Reimprime la meta actual usando print

w Write goal

Escribe la meta actual en la terminal usando write. Esto puede ser muy útil cuando la lista temporal de rutinas no hace lo que se espera de ella

d Display goal

Despliega la meta en turno, usando display (ver Write arriba).

g Print ancestor goals

Provee al usuario de una lista de las metas o submetas que ya se han cumplido en la ejecución de un programa, el orden se da en forma jerárquica hacia arriba de la secuencia que ha sido llamada. Usa los ancestros de cada predicado evaluable (ver pág. 42). Si se proporciona un entero con esta directiva entonces se despliegan n antecesores desde el punto en que el usuario ha dado la directiva.

@ Accept command

Permite llamar arbitrariamente a metas de Prolog, es un break de un solo paso muy efectivo, (ver más adelante). El mensaje inicial "| :- " saldrá en la terminal y el comando entonces se lee desde la terminal y se ejecuta como si el usuario estuviera en el nivel más alto.

b Break

Llama al predicado evaluable break, pone al intérprete al nivel más alto con la ejecución aparte del modulo actual, cuando se finaliza el modo break (^Z) el sistema pregunta en qué punto se hizo el rompimiento, y una nueva ejecución se separa de la suspendida, los números de invocación comienzan a partir de 1 desde su invocación.

[ Consult user Permite insertar clausulas y regresar a donde se estaba, es lo mismo que usar "@" seguida de "[user].".

Page 22: Tutorial de prolog

n noebug

Desactiva el modo debug, lo pone en Off, es la forma correcta de apagar el debug desde el modo de rastreo. No se puede usar "@" o "b" porque siempre activa el debug desde el regreso.

2.7 Consultar de nuevo durante la depuración.

Es posible y a veces es útil reconsultar o releer un archivo a medias de la ejecución de un programa. Sin embargo, esto puede ocasionar que el sistema funcione de una manera impredecible debido a las siguientes circunstancias: un procedimiento se ha ejecutado con éxito, se ha redefinido por una consulta y después se ha reactivado por un backtracking. Cuando el backtracking ocurre, todas las clausulas nuevas aparecerán en el intérprete como posibilidades de alternativa de solución aún cuando se hayan usado ya clausulas o predicados exactamente iguales, así grandes cantidades de procedimientos no deseados ocasionarían backtracking. Este problema se evita si se hace el consult desde el puerto del procedimiento que será redefinido.

CAPITULO 3. COMPILACIÓN

El compilador Prolog DECsystem-10 produce código más compacto y eficiente, el cual funciona de 10 a 20 veces más rápido que el intérprete de código y requiere mucho menos almacenamiento en tiempo de ejecución. Los programas compilados con Prolog, son comparados en eficiencia, con programas desarrollados en LISP ( con el compilador LISP DECsystem-10 ), para realizar las mismas funciones. En contra de esto, la desventaja es que la compilación es varias veces más lenta que la "consulta" y mucha de la ayuda proporcionada por el depurador, como el seguimiento, no es aplicable al código compilado.

Page 23: Tutorial de prolog

3.1 Llamando al Compilador.

Para compilar el programa se usa la siguiente sentencia :

|? compile (Archivos).

donde "Archivos" es cualquier nombre de archivo ( incluyendo el archivo ‘user’ ), o una lista de los nombres de los archivos que se desean compilar. Con esta instrucción, los procedimientos contenidos en dichos archivos serán compilados. A continuación se muestra un ejemplo con varios archivos :

|? compile ( [dbase, 'extras.pl,' user] ).

Exteriormente, el efecto de "compile" es mucho muy parecido al de "reconsult". Si las cláusulas para algunos predicados aparecen en más de un archivo, el juego posterior de dichas cláusulas, sobre-escribe el anterior. La división del programa en archivos separados no implica ninguna estructura modular ( ningún procedimiento compilado puede llamar a cualquier otro ).

3.2 Declaraciones Públicas.

Para hacer que un procedimiento compilado sea accesible por el interprete de código ( incluyendo directivas ) es necesario declarar dicho procedimiento como público, lo cual se realiza con el comando siguiente del compilador :

:- public Predicates.

donde "Predicates" es una especificación del predicado, en la forma : Nombre / Arity , o también puede ser una conjunción de especificaciones semejantes, por ejemplo :

:- public concatenate/3, member/2, ordered/1, go/0.

Las declaraciones públicas pueden aparecer en cualquier parte dentro de un archivo compilado; no es necesario que las declaraciones públicas procedan al correspondiente procedimiento, ni tampoco que estén dentro del mismo archivo.

Page 24: Tutorial de prolog

3.3 Mezclando Código Compilado y Código Interpretado.

Para predicados públicos un procedimiento compilado sobre-escribe cualquier versión interpretada previa ( cf. reconsult ). De la misma forma una reconsulta subsecuente de la versión interpretada sobre-escribirá la versión compilada.

Es posible tener un procedimiento compilado con el mismo nombre y arity, así como muchos y diferentes procedimientos interpretados. Suponiendo que el procedimiento compilado no fue declarado para ser público, los dos procedimientos nunca intervendrán el uno con el otro, es decir, el código compilado utilizará la versión compilada mientras que el interpretador de código usará la versión interpretada.

Cuando una versión compilado se hace presente y sobre-escribe un procedimiento interpretado, éste último es reemplazado por la cláusula :

P :- incore(P).

donde "P" es el objetivo más general para el predicado y "encore" es un predicado evaluable estándar ( análogo a "call" ), por medio del cual son accesibles todos los procedimientos compilados para los predicados públicos.

Hay dos formas en que el código compilado puede utilizar procedimientos interpretados :

• Si no hay cláusulas compiladas para el predicado, el procedimiento interpretado es llamado automáticamente.

• La sentencia : call( P ) , siempre llama al intérprete. Es importante hacer notar, que esto implica que deseamos llamar procedimientos compilados con "call" , deben ser declarados como públicos.

Para aclarar lo anterior vamos a ver un ejemplo :

Primero compilamos el siguiente archivo: :- public f/1, g/1.

f(a).

g(X) :- f(X).

g(X) :- h(X).

No hay cláusulas para h/1. Después consultamos lo siguiente :

f(b).

h(c).

Page 25: Tutorial de prolog

Ahora, si nosotros llamamos a " f "obtendremos :

?- f(X).

X = a ;

X = b ;

no

Esto es, nosotros usamos tanto las cláusulas compiladas, como las interpretadas para " f ". De cualquier modo si llamamos a " g " :

?- g(X).

X = a ;

X = c ; no

podemos observar que " g " solo llama la versión compilada de " f ", así que la solución : X = b , no es encontrada. La segunda cláusula para " g " llama a " h ", y como no hay cláusulas compiladas para " h " esta llamada es pasada al intérprete, el cual encuentra una solución : X = c.

3.4 Declaraciones de Modo.

Cuando un programa va a ser compilado es conveniente incluir declaraciones de modo, las cuales informan al compilador que ciertos procedimientos serán usados en forma restringida, es decir, que algunos argumentos en la llamada siempre serán "entrada", mientras que otros siempre serán "salida". Esta información habilita al compilador para ser capaz de generar código más compacto y hacer un mejor uso del almacenamiento en tiempo de ejecución. Las declaraciones de modo también ayudan a que otras personas comprendan la operación del programa.

Una declaración de modo es indicada con una directiva del compilador de la forma :

:- mode P(M).

donde "P" es el nombre de un procedimiento y "M" especifica los "modos" de sus argumentos. M consiste de cierto número de "elementos de modo" ( elementos que indican el modo deseado para los argumentos ), separados por comas, una para cada

Page 26: Tutorial de prolog

posición de los argumentos del predicado interesado. Los elementos de modo pueden ser de cualquiera de los 3 tipos siguientes :

• Modo + : Especifica que el argumento correspondiente en cualquier llamada al procedimiento siempre será "entrada".

• Modo - : Indica que el argumento nunca será "entrada", es decir, siempre será "salida".

• Modo ? : Indica que no hay restricción en la forma del argumento. Por ejemplo, una declaración de modo como :

:- mode concatenate(?,?,?).

es equivalente a omitir completamente la declaración.

Por ejemplo, si tenemos un procedimiento llamado "encadenar" y los dos argumentos primeros, siempre serán "entradas", podemos dar la siguiente declaración de modo :

:- mode encadenar(+,+,?).

En el caso de que en el procedimiento anterior el tercer argumento siempre será "salida", podemos reforzar la declaración de modo como sigue :

:- mode encadenar(+,+,-).

Además, está permitido combinar varias declaraciones de modo en un solo comando, por ejemplo :

:- mode encadenar(+,+,-), mode miembro(+,?), mode orden(+).

Para que una declaración de modo tenga efecto debe aparecer antes de las cláusulas del procedimiento correspondiente. Cuando una declaración de modo es violada por una llamada a un procedimiento, la forma precisa como reacciona depende de el objetivo y la cabecera de la cláusula. La llamada puede ser realizada con éxito como si no hubiera una declaración de modo o puede causar un mensaje de error y fracasar. Como el resultado preciso depende de los cambios en las versiones futuras del sistema, el usuario debió asumir que todas las declaraciones de modo que son violadas causaran un mensaje de error.

Las declaraciones de modo son ignoradas por el intérprete.

Page 27: Tutorial de prolog

3.5 Indexación.

En contraste con el interprete, las cláusulas de un procedimiento compilado son indexadas de acuerdo al "functor" principal del primer argumento especificado en la cabecera de la cláusula. Esto significa que el subconjunto de cláusulas, son emparejadas con un determinado objetivo, hasta que el primer paso de la unificación interesada, sea hallada muy rápidamente, prácticamente en un tiempo constante ( un tiempo independiente del número de cláusulas ). Esto puede ser muy importante donde hay un gran número de cláusulas en un procedimiento.

La indexación también mejora la capacidad del sistema de Prolog para detectar la determinacia, la cual es importante en la conservación del espacio de trabajo.

3.6 Optimización de la cola de Recursión.

El compilador incorpora "Optimización de la cola de recursión" para mejorar la velocidad y la eficiencia del espacio de procedimientos determinados. Cuando la ejecución alcanza el último objetivo en una cláusula que pertenece a un procedimiento, y provee que no hay puntos de retro seguimiento restantes, todo el espacio de trabajo asignado a los procedimientos locales es reclamado ANTES de finalizar la llamada y cualquier estructura que fue creada es eliminada. Esto significa que el programa puede realizar varias recursiones sin necesidad de exceder el límite en el espacio.

Por ejemplo :

cycle(State) :- transform(State,State1), cycle(State1).

donde "transform" es un procedimiento determinado, puede continuarse ejecutando indefinidamente, previendo que cada estructura individual "State", no sea demasiado larga. El procedimiento "cycle" es equivalente a un lazo iterativo de in lenguaje convencional.

Para lograr la ventaja de la optimización de la cola recursiva, debemos asegurarnos que Prolog pueda reconocer que el procedimiento es determinado en el punto donde la llamada recursiva toma lugar. Dicho en otra forma, el sistema debe ser capaz de detectar que no hay otras soluciones que deben ser encontradas para el objetivo actual, mediante subsecuente retro seguimiento. En general esto está envuelto en la indexación del compilador Prolog DEC-10 y / o el uso de corte.

Page 28: Tutorial de prolog

3.7 Limitaciones Prácticas.

Algunas consideraciones que debemos tomar en cuenta son :

• Hay que tener presente que el espacio ocupado por el código compilado, cuando es reemplazado, no es reclamado.

• Hacer que un predicado sea público, nos lleva a generar una cantidad significante de código extra. Además este código es regenerado cada vez que el compilador es llamado ( con lo cual el espacio ocupado por el código anterior no es reclamado ). Debido a esto, es conveniente, cuando sea posible, incluir en una sola llamada al compilador, todos los archivos que se desea sean compilados.

• La ejecución del código compilado no puede ser "rota" o "abortada", lo cual si s posible con el código interpretado. Una respuesta a la petición para "abortar", realizada con CTRL+C, solo tiene efecto en la siguiente entrada de un procedimiento interpretado.

Se puede notar que hay una pausa despreciable en el inicio y terminación de la compilación. Esto se debe a que el compilador reside en una capa separada, la cual debe ser intercambiada.