Documentos de Académico
Documentos de Profesional
Documentos de Cultura
3848 0 Prolog
3848 0 Prolog
INTRODUCCION
Indice
1. Introducci
on
2. Caractersticas Generales
2.3.2. SWI-Prolog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1. Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1. Terminos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.2. Programas
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
4.1. Aritmetica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.1.1. Operadores aritmeticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.1.2. Predicados aritmeticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.1.3. Programas aritmeticos en Prolog . . . . . . . . . . . . . . . . . . . . . . . 17
4.2. Entrada/Salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.3. Control: el corte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.3.1. Definicion y propiedades . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.3.2. Usos del corte. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.
Introducci
on
En este documento se realiza una breve introduccion al lenguaje de programacion Prolog, con
el objetivo fundamental de mostrar como se da el paso desde el concepto de programacion
logica pura estudiado en el tema anterior a un lenguaje de programacion real. En efecto,
el lenguaje Prolog se puede ver como una extensi
on de la programacion logica pura, en el
sentido de que, ademas de permitir programar de acuerdo con el paradigma de la programacion
logica, incorpora una serie de elementos adicionales cuyo objetivo es ofrecer una herramienta
de programacion que sea u
til en la practica.
Despues de una descripcion de las caractersticas generales del lenguaje (evolucion historica,
esquema general de trabajo e implementaciones existentes), el apartado 3 estudia el funcionamiento de Prolog desde el punto de vista de la programacion logica pura, basado en la Programacion Logica Definida estudiada en el tema anterior. Su extension, que se realiza mediante la
introduccion de los denominados predicados predefinidos, se resume en el apartado 4.
No se abordan, por lo tanto, mas que algunos de los aspectos mas relevantes del lenguaje.
Para un estudio mas en profundidad de Prolog se recomienda consultar los siguientes libros (los
tres primeros son libros de caracter introductorio, mientras que el u
ltimo trata aspectos mas
avanzados).
L. Sterling and E. Shapiro. The Art of Prolog. The MIT Press, Cambridge, Mass., second
edition, 1994.
W.F. Clocksin and C.S. Mellish. Programming in Prolog. Springer-Verlag, Berlin, fourth
edition, 1994.
I. Bratko. Prolog Programming for Artificial Intelligence. Addison-Wesley, Reading, Massachusetts, third edition, 2001.
R. OKeefe. The Craft of Prolog. The MIT Press, Cambridge, MA, 1990.
2.
2.1.
Caractersticas Generales
Evoluci
on hist
orica
Prolog (del frances, PROgrammation en LOGique) fue el primer lenguaje de programacion basado en el paradigma de la programacion logica. Se implemento por primera vez a principios de
los a
nos setenta en la Universidad de Marsella (Francia), por un equipo dirigido por A. Colmeraeur, utilizando resultados teoricos aportados por R. Kowalski (Universidad de Edimburgo).
Aunque con ciertas dificultades iniciales, debido principalmente a la novedad del paradigma
y a la escasa eficiencia de las implementaciones disponibles, el lenguaje se fue expandiendo
rapidamente, sobre todo en Europa y en Japon (en este u
ltimo pas la programacion logica
se incluyo como parte central del proyecto de ordenadores de quinta generacion de los a
nos
ochenta). En 1995 el lenguaje se normaliza con el correspondiente estandar ISO. En la actualidad Prolog se ha convertido en una herramienta de desarrollo de software practica y de gran
aceptacion para la que se dispone de m
ultiples compiladores, tanto comerciales como de libre
distribucion.
2
2.2.
En cualquiera de los dos casos, la respuesta del sistema puede ser de dos tipos:
- si se ha detectado alg
un error sintactico en el programa, el sistema Prolog avisa de ello
mediante un mensaje. En estos casos habra que volver a editar el programa para
corregir los errores que contiene y volver a cargarlo.
- en caso contrario, el sistema responde con la palabra yes, que indica que el programa
se ha cargado correctamente y el sistema esta listo para recibir consultas del usuario.
Nota: la mayora de los sistemas Prolog ofrecen un entorno de programacion interactivo
dotado de men
us mediante los cuales se ofrecen las acciones mas habituales, entre ellas la
carga de ficheros tanto para ser interpretados como para ser compilados (normalmente,
File/Consult... y File/Compile...).
3. Activar un programa desde el sistema Prolog.
Una vez que se ha cargado correctamente un programa, el sistema esta preparado para
recibir las consultas del usuario relativas al problema que se pretende resolver. Para ello
bastara con escribir dichas consultas, siguiendo la sintaxis especfica del lenguaje, en
la lnea de consultas del sistema. El formato de estas consultas as como las posibles
reacciones del sistema ante ellas se detallan mas adelante.
on que permite
Nota: Los sistemas Prolog suelen incorporar un mecanismo de depuraci
seguir paso a paso la ejecucion de las consultas, establecer puntos de corte en la ejecucion,
etc. Su funcionamiento basico es el siguiente:
para entrar en modo de depuracion debe escribirse el predicado del sistema trace
en la lnea de consultas:
?- trace.
A partir de ese momento, las consultas que se realicen se ejecutaran en modo de
depuracion, mostrando una despues de otra todas las llamadas realizadas internamente por el sistema Prolog. Para avanzar en la depuracion, basta con pulsar la tecla
RETURN. Tambien puede pulsarse la tecla h para obtener ayuda sobre las distintas opciones disponibles. En particular, la tecla n permite abandonar el proceso de
depuracion.
para desactivar el modo de depuracion se utiliza el predicado notrace:
?- notrace.
Para una informacion mas detallada sobre el mecanismo de depuracion de Prolog se
recomienda leer el captulo 8 del libro de Clocksin y Mellish citado en la introduccion o
consultar el manual de referencia del sistema Prolog que se este utilizando.
4. Salir del sistema Prolog.
Para salir del sistema Prolog basta con escribir el predicado del sistema halt en la lnea
de comandos:
?- halt.
2.3.
Implementaciones de Prolog
por lo que los programas Prolog que se generen de acuerdo con dicho estandar podran ejecutarse
en cualquiera de estos sistemas.
En esta asignatura (y en este documento) se va a utilizar la implementacion comercial denominada SICStus Prolog, para la cual la URJC dispone de una licencia de Campus. A
continuacion se describe brevemente tanto este sistema como otro sistema Prolog, de dominio
p
ublico, denominado SWI-Prolog.
2.3.1.
SICStus Prolog
SWI-Prolog
consta de un u
nico fichero ejecutable que instala automaticamente el sistema. Este
se utiliza de
acuerdo con el esquema general de trabajo en Prolog descrito mas arriba.
3.
Como se ha visto previamente, existen muchos modelos distintos de programacion logica, que
se distinguen dependiendo del tipo de Logica utilizada para representar el conocimiento y del
mecanismo de demostracion automatica elegido. El lenguaje Prolog se basa en la Programaci
on
L
ogica Definida estudiada en el tema anterior, pero con ciertas peculiaridades, tanto sintacticas
como semanticas, que se discuten a continuacion.
3.1.
Sintaxis
3.1.1.
T
erminos
Al igual que en Logica de Primer Orden, los terminos en Prolog se clasifican en tres categoras:
constantes, variables y terminos compuestos.
Constantes
Prolog distingue dos tipos de constantes:
N
umeros. Este tipo de constantes se utilizan para representar tanto n
umeros enteros como
n
umeros reales y poder realizar con ellos operaciones aritmeticas.
- La representacion mas corriente de los n
umeros enteros es la notacion decimal habitual
(por ejemplo 0, 1, -320, 539, etc) aunque tambien se pueden representar en otras
bases no decimales.
- Los n
umeros reales se pueden representar tanto en notacion decimal (por ejemplo
1.0, -3.14) como en notacion exponencial (por ejemplo 4.5E6, -0.12e+3, 12.0e-2). En
ambos casos debera haber siempre por lo menos un dgito a cada lado del punto.
Atomos.
Los atomos (no confundir con las formulas atomicas de la LPO) se utilizan para
dar nombre a objetos especficos, es decir, representan individuos concretos. Existen tres
clases principales de atomos:
- cadenas formadas por letras, dgitos y el smbolo de subrayado, que deben empezar
necesariamente por una letra min
uscula.
Cadenas validas: f, pepe1, libro33a, libro_blanco.
Cadenas no validas: 1libro, libro-blanco, _hola, Libro.
- cualquier cadena de caracteres encerrada entre comillas simples.
Ejemplos: SICStus Prolog, Libro-blanco, 28003 Madrid.
Estos atomos son u
tiles cuando se necesita trabajar con constantes que empiecen por
una letra may
uscula o por un dgito.
- existe ademas otro tipo de atomos, compuestos por combinaciones especiales de signos,
de uso menos com
un.
Variables
Las variables en Prolog se representan mediante cadenas formadas por letras, dgitos y el smbolo
de subrayado, pero deben necesariamente empezar por una letra may
uscula o por un smbolo de
subrayado.
Ejemplos: X, Resultado_1, Entrada, _total3, _3bis, _
Las variables que empiezan con un smbolo de subrayado, _, se denominan variables an
onimas,
y se usan cuando se necesita trabajar con variables cuyos posibles valores no interesan. Su
utilidad se describira mas adelante al analizar la construccion de programas y consultas.
T
erminos Compuestos
Los terminos compuestos, o estructuras, se construyen mediante un smbolo de funcion, denominado functor, que se denota mediante un atomo, seguido, entre parentesis, por una serie de
terminos separados por comas, denominados argumentos.
Ejemplos: fecha(1,mayo,2001), punto(X,Y), recta(punto(1,2), punto(3,5))
Nota: al escribir un termino compuesto, no puede haber ning
un espacio entre el functor y
el parentesis abierto previo a los argumentos. Por ejemplo, punto (X,Y) no es un termino
compuesto correcto y producira un error de compilacion.
Prolog tambien permite escribir ciertos terminos compuestos en forma de operadores, generalmente en notacion infija en el caso de functores de aridad 2 y en notacion prefija o postfija en
el caso de functores de aridad 1. Este es el caso de los operadores aritmeticos predefinidos de
Prolog, que se mencionaran mas adelante, y que permiten escribir terminos compuestos de la
forma X+Y o -X en lugar de +(X,Y) o -(X). El programador tambien puede definir sus propios
operadores.
Uno de los terminos compuestos mas importantes y u
tiles que ofrece Prolog son las listas,
secuencias ordenadas de cero o mas elementos, donde los elementos pueden ser cualquier tipo
de termino. Prolog representa las listas teniendo en cuenta su estructura recursiva:
- la lista vaca se representa mediante el atomo [].
7
- toda lista no vaca tiene una cabeza (que sera cualquier termino) y un resto (que sera una
lista), y se representa mediante un termino compuesto de aridad 2, cuyo functor es un
punto y cuyos argumentos son, respectivamente, la cabeza y el resto de la lista.
Ejemplos: la lista compuesta por un u
nico elemento, la constante a, se representa como (a, []).
La lista compuesta por los elementos a, b y c se corresponde con la estructura (a, (b, (c, []))).
Dado que la notacion anterior puede resultar incomoda a la hora de escribir listas complicadas,
Prolog admite tambien una notacion mas sencilla que consiste en enumerar entre corchetes
todos los elementos de la lista, separados por comas. Con esta notacion, las dos listas del
ejemplo anterior se representaran, respectivamente, como [a] y [a, b, c]. Prolog tambien dispone
de otra notacion para las listas, que consiste en representar la lista con cabeza X y resto Y
mediante el termino [X|Y ]. Esta u
ltima notacion es fundamental para poder separar la cabeza
del resto de una lista. Su utilidad en la practica se vera en las clases de problemas.
3.1.2.
Programas
Los programas Prolog son programas logicos definidos, y estan por lo tanto compuestos por
una serie de clausulas de Horn positivas, esto es, hechos y reglas. Hay que tener en cuenta sin
embargo las siguientes diferencias en cuanto a la notacion empleada en la Programacion Logica
Definida:
Los smbolos de predicado se denotan mediante a
tomos, por lo que no pueden empezar,
como ocurre en Programacion Logica, mediante una letra may
uscula. Observese por lo
tanto que el lenguaje Prolog no distingue entre smbolos de predicado, smbolos de funcion y constantes, puesto que todos ellos se representan mediante atomos (el compilador
distingue unos de otros dependiendo del contexto en el que aparecen). Para referirse a un
predicado nombre_predicado se suele emplear la notacion nombre_predicado/n, donde
n indica el n
umero de argumentos del predicado.
Los hechos deben terminar con un punto y omitir el smbolo utilizado en Programacion Logica. As, el hecho A se escribe en Prolog de la forma A..
Las reglas deben tambien terminar con un punto y sustituir el smbolo de la Programacion Logica por el smbolo :-. As, la regla A A1 , . . . , An se escribe en Prolog
de la forma A :- A1 , . . . , An ..
Nota: Al trabajar en Prolog se suele aplicar un convenio estandar para describir el uso de
los predicados (tanto predefinidos como definidos por el programador): en los programas, las
clausulas de cada predicado deberan ir precedidas de un comentario incluyendo una lnea que
describa su uso, as como una descripcion en lenguaje natural de su cometido. El convenio para
describir el uso de un predicado es el siguiente:
nombre_predicado(#NomVar_1, ...., #NomVar_n)
donde NomVar_1, ..., NomVar_n son nombres de variables y el smbolo #, que sirve para indicar
como debe usarse el argumento correspondiente al realizarse una consulta, puede tomar uno de
los tres siguientes valores:
8
+ para indicar que el argumento correspondiente debe estar, en la consulta, instanciado con un
termino no variable (este tipo de argumentos se corresponden por lo tanto con parametros
de entrada).
- para indicar que el argumento correspondiente no debe estar instanciado en la consulta, es
decir, debe ser una variable (este tipo de argumentos se corresponden por lo tanto con
parametros de salida).
? para indicar que el argumento puede estar tanto instanciado como no instanciado (es decir,
se trata de parametros que se pueden usar tanto para entrada como para salida).
Por ejemplo, el predicado predefinido sort/2, que sirve para ordenar listas de elementos, se describe como sort(+Lista1, ?Lista2), indicando as que para utilizarlo el primer argumento debe estar instanciado (debe ser una lista concreta). Por lo tanto, el predicado sort/2 se podra utilizar en consultas de la forma ?- sort([c,v,a], X). o ?- sort([c,v,a], [a,c,v]).,
pero si se intenta hacer una consulta en la que el primer argumento no este instanciado, como
por ejemplo ?- sort(X, [c,v,a])., se producira un error.
Ejemplo: Los programas logicos para el calculo del factorial y la suma de n
umeros naturales
estudiados en el tema anterior eran programas logicos definidos, por lo que se pueden convertir
facilmente en programas Prolog con solo adaptar su sintaxis de acuerdo con lo establecido mas
arriba (ficheros factorial.pl y suma.pl):
PROGRAMA LOGICO
DEFINIDO
PROGRAMA PROLOG
% factorial(?X,?Y)
% cierto si Y es el factorial de X
factorial(0,s(0)).
factorial(s(X),s(X)*Y) :- factorial(X,Y).
% suma(?X,?Y,?Z)
% cierto si Z es la suma de X e Y
suma(X,0,X).
suma(X,s(Y),s(Z)) :- suma(X,Y,Z).
Factorial(0,s(0))
Factorial(s(x),s(x)*y) Factorial(x,y)
Suma(x,0,x)
Suma(x,s(y),s(z)) Suma(x,y,z).
Observese que en los ejemplos anteriores las variables X, Y y Z aparecen a ambos lados de las
respectivas reglas. Existen sin embargo casos en los que una variable aparece solo a un lado de
una regla y sus posibles valores no tienen importancia. En estos casos no es necesario pensar
un nombre de variable sino que basta con usar la variable anonima _.
Ejemplo: Supongase que, dado el predicado factorial, se necesita definir a partir de el un
predicado es_factorial(X) que es cierto si X es el factorial de alg
un n
umero natural. Una
forma de definir este predicado sera estableciendo que X es un factorial siempre y cuando
exista alg
un Y tal que X sea el factorial de ese Y , es decir:
% es_factorial(?X): cierto si X es el factorial de alg
un n
umero
es_factorial(X) :- factorial(Y,X).
Sin embargo, en este caso el posible valor de la variable Y es indiferente, puesto que lo u
nico
que se quiere saber es si X es el factorial de alg
un n
umero, sin importar quien sea este. As, la
definicion anterior se podra sustituir por:
es_factorial(X) :- factorial(_,X).
9
3.1.3.
Resumen
Como se acaba de ver, la sintaxis del lenguaje Prolog, aunque se basa en la sintaxis de la Programacion Logica Definida, presenta ciertas diferencias respecto a esta u
ltima. A continuacion
se resumen las mas importantes:
- los smbolos de variable se escriben empezando por una letra may
uscula o por un smbolo de
subrayado.
- los smbolos de predicado se escriben empezando por una letra min
uscula, al igual que los
smbolos de funcion y las constantes.
10
NOTACION
clausular
logica estandar
prog. logica
Prolog
3.2.
REGLAS
{A1 , . . . , An , A}
x1 . . . xp [(A1 . . . An ) A]
A A 1 , . . . , An
A :- A1 , . . . , An .
HECHOS
{A}
x1 . . . xp (A)
A
A.
METAS
{A1 , . . . , An }
x1 . . . xp (A1 . . . An )
A 1 , . . . , An
?- A1 , . . . , An .
Sem
antica
?- X = f(X).
X = f(f(f(f(f(f(f(f(f(f(...)))))))))) ?
yes
% no se realiza el test de ocurrencia
Nota: algunas implementaciones de Prolog, como por ejemplo SICStus Prolog, incorporan
un predicado predefinido especial que permite unificar con test de ocurrencia. En SICStus
Prolog este predicado se denomina unify_with_occurs_check.
?- unify_with_occurs_check(X,f(X)).
no
% s
se realiza el test de ocurrencia
Funci
on de selecci
on. Selecciona siempre el literal mas a la izquierda.
Regla de ordenaci
on. Elige las clausulas de acuerdo con el orden en el que estas aparecen
en el programa.
Estrategia de b
usqueda: b
usqueda en profundidad (se recuerda que esta estrategia es muy
eficiente pero tiene el inconveniente de que no es completa, esto es, puede conducir a una
computacion infinita a
un en el caso de que exista una solucion).
Por lo tanto, cuando, una vez cargado un programa logico, se realiza una consulta, Prolog
construye el arbol de Resolucion SLD correspondiente, de acuerdo con la funcion de seleccion y
la regla de ordenacion citadas, haciendo un recorrido en profundidad. As, las posibles respuestas
del sistema ante una consulta son las siguientes:
Si todas las ramas del arbol SLD son ramas fallo, la respuesta del sistema sera no.
Si el arbol SLD tiene alguna rama infinita m
as a la izquierda que cualquier posible rama
exito, la reaccion del sistema dependera de la implementacion concreta de Prolog que se
este utilizando. Algunos sistemas, como por ejemplo SWI-Prolog, presentan un mensaje
de error. Otros, como es el caso de SICStus Prolog, no responden, por lo que es necesario
interrumpir la ejecucion de la consulta mediante CONTROL-C (si se esta usando SICStus
Prolog a traves de Emacs, la composicion de teclas CONTROL-C debera efectuarse dos
veces seguidas). Una vez interrumpida la ejecucion, el sistema muestra por pantalla el
mensaje:
Prolog interruption (h for help)?
y acepta a continuacion una letra que determine la accion a seguir. Las posibles acciones
se pueden consultar pulsando la tecla h, aunque lo mas habitual sera contestar con la
letra a, cuya accion asociada es abortar la ejecucion de la consulta.
En otro caso (es decir, cuando el arbol de Resolucion tiene por lo menos una rama exito
mas a la izquierda que cualquier posible rama infinita):
- Si la consulta realizada no tiene variables (o las que tiene son anonimas), la respuesta
del sistema sera yes, terminandose as la ejecucion de la consulta.
- Si la consulta realizada tiene alguna variable no anonima, el sistema muestra por
pantalla los valores de las variables que se corresponden con la primera rama exito
encontrada al buscar en profundidad en el arbol de Resolucion, y queda a la espera
de nuevas instrucciones por parte del usuario:
12
progenitor(X,Y) :- hijo(Y,X).
hijo(A,B) :- progenitor(B,A).
puesto que cualquier consulta a uno de los dos predicados anteriores provocara necesariamente
un bucle infinito.
4.
Predicados Predefinidos
4.1.
4.1.1.
Aritm
etica
Operadores aritm
eticos
Prolog tiene predefinidos los operadores aritmeticos mas habituales, mediante los que se pueden
formar expresiones aritmeticas. A continuacion se enumeran algunos de los mas importantes:
X+Y
X-Y
X*Y
X/Y
X//Y
X mod Y
abs(X)
sqrt(X)
log(X)
suma de X e Y
X menos Y
producto de X por Y
cociente real de la division de X por Y
cociente entero de la division de X por Y
resto de la division entera de X por Y
valor absoluto de X
raz cuadrada de X
logaritmo neperiano de X
Tengase en cuenta que los operadores anteriores permiten simplemente construir expresiones
aritmeticas, pero estas no son mas que estructuras (terminos compuestos) que no representan
ning
un valor. Por ejemplo, la expresion 3+5 no es mas que el termino compuesto +(3,5) escrito
en notacion infija. As, no es posible hacer consultas del estilo ?- 3+5., puesto que + no
es un predicado, y si se hiciese la consulta ?- 3+5 = 8., la respuesta de Prolog sera no,
dado que el termino compuesto +(3,5) no es unificable con el termino constante 8.
Para poder evaluar expresiones aritmeticas en Prolog hay que utilizar los predicados aritmeticos
que se describen a continuacion.
15
4.1.2.
Predicados aritm
eticos
Los predicados aritmeticos predefinidos de Prolog se utilizan para evaluar expresiones aritmeticas. El mas habitual es el predicado predefinido is, que se usa en notacion infija de la siguiente
forma:
X is Y
A la hora de usar este predicado hay que tener en cuenta las siguientes consideraciones:
1. Su uso puede dar lugar a un error en los dos siguientes casos:
a) cuando la parte derecha no es una expresion aritmetica:
?- X is a+1.
{DOMAIN ERROR: _157 is a+1 - arg 2: expected expression, found a}
b) cuando la parte derecha es una expresion aritmetica pero no se puede evaluar:
?- X is 4*Z.
{INSTANTIATION ERROR: _157 is 4*_155 - arg 2}
2. Salvo en los casos anteriores, el resultado del predicado dependera de si la parte izquierda
unifica o no con el resultado obtenido al evaluar la parte derecha:
?- X is sqrt(4).
X = 2.0 ?
yes
?- 5 is 2+3.
yes
?- X is 5, Y is X+1.
X = 5, Y = 6 ?
yes
?- 3+5 is 3+5.
no
=:= Y
=\= Y
< Y
=< Y
> Y
>= Y
cierto
cierto
cierto
cierto
cierto
cierto
si
si
si
si
si
si
?- 3+5 =:= 8.
yes
?- 1+ 5 > abs(-8).
no
?- 3 =\= 3*a.
.. ERROR: ..
Programas aritm
eticos en Prolog
De forma similar, una definicion mas eficiente -aunque menos elegante- del predicado factorial
sera la siguiente:
% factorial(+X, ?Y): cierto si Y es el factorial de X.
factorial(0, 1).
factorial(X, Y) :X >0, X1 is X-1, factorial(X1, FactX1), Y is X*FactX1.
Por el mismo motivo que antes, el predicado anterior solo se podra usar cuando el primer
argumento este instanciado, es decir, el predicado es valido para calcular factoriales, pero no
sirve ya para averiguar si un n
umero es o no el factorial de alg
un otro n
umero. Esta restriccion
no solo afecta al uso directo del predicado, sino tambien a su capacidad para ser usado en la
definicion de otros: por ejemplo, el predicado es_factorial que se describio en el apartado
2.1.2. a partir de la version logica del predicado factorial ya no podra definirse utilizando
esta nueva version. Observese asimismo que en la nueva version se ha introducido, antes de la
llamada recursiva, la comprobacion X>0, necesaria si se quiere evitar que se produzca una rama
infinita en el arbol de Resolucion SLD correspondiente (constr
uyanse como ejercicio los arboles
de Resolucion asociados a una consulta concreta con y sin la comprobacion anterior).
4.2.
Entrada/Salida
El lenguaje Prolog ofrece toda una serie de predicados predefinidos para la realizacion de operaciones de entrada/salida. Se trata de predicados que no tienen sentido desde un punto de vista
puramente logico, sino que producen un efecto colateral (escritura/lectura de alg
un termino,
apertura/cierre de un fichero, etc). A continuacion se describen algunos de los predicados de
entrada/salida mas basicos:
open(+NombreFichero, +Modo, -Fichero)
Si NombreFichero es un nombre de fichero valido, abre el fichero correspondiente de
acuerdo con el modo especificado por Modo, y unifica con Fichero el identificador del
fichero abierto. Los valores para el argumento Modo pueden ser:
read para abrir el fichero en modo lectura.
write para abrir el fichero en modo escritura (si el fichero no existe, lo crea; si ya existe,
su contenido se perdera).
append para abrir el fichero en modo escritura (si el fichero no existe, lo crea; si ya existe,
las operaciones de escritura se realizaran al final del fichero).
close(+Fichero)
Cierra el fichero asociado con el identificador Fichero.
set_input(+Fichero)
set_output(+Fichero)
current_output(?Fichero)
Nota: el fichero por defecto, tanto para lectura como para escritura, es la pantalla, cuyo
identificador es user.
read(?Termino)
read(+Fichero, ?Termino)
Lee el siguiente termino (del fichero de lectura actual o del fichero especificado, previamente abierto en modo lectura) y unifica el resultado con Termino. El termino debe acabar
con un punto y un retorno de carro.
write(?Termino)
write(+Fichero, ?Termino)
nl(+Fichero)
Escribe un retorno de carro en el fichero de escritura actual o en el fichero especificado
(previamente abierto en modo escritura).
Ejemplos: Se incluyen a continuacion algunos ejemplos tpicos de predicados que realizan operaciones de entrada/salida (fichero entrada-salida.pl):
% pide_numero(-X)
% X es un n
umero le
do del fichero de lectura actual
pide_numero(X) :write(Introduzca un n
umero: ),
nl,
read(X).
% escribe_cuadrado(+X)
% escribe el cuadrado de X en el fichero de escritura actual
escribe_cuadrado(X) :X2 is X*X,
write(El cuadrado de ),
write(X),
write( es ),
write(X2).
% pide un n
umero y escribe su cuadrado por pantalla
cuadrado :pide_numero(X),
escribe_cuadrado(X).
% imprime_lista(+Fichero, +L)
% Si Fichero es un identificador de fichero y L es una lista,
% escribe los elementos de L en el fichero, uno por l
nea
imprime_lista(_Fichero, []).
imprime_lista(Fichero, [C|R]) :write(Fichero, C),
nl(Fichero),
imprime_lista(Fichero, R).
19
% imprime_lista(+L)
% Si L es una lista, imprime por pantalla sus elementos, uno por l
nea
imprime_lista(L) :imprime_lista(user, L). % user es el identificador de la pantalla
% pide una lista y la imprime en un fichero
prueba_fich :write(Introduzca una lista: ), nl,
read(Lista),
open(prueba.txt, write, Prueba),
imprime_lista(Prueba, Lista),
write(la lista se ha escrito en el fichero prueba.txt),
close(Prueba).
A continuacion se reproduce la ejecucion de algunos de los predicados anteriores:
?- cuadrado.
Introduzca un n
umero:
|: 3.
El cuadrado de 3 es 9
yes
?- imprime_lista([esto,es,una,lista]).
esto
es
una
lista
yes
?- prueba_fich.
Introduzca una lista:
|: [h,o,l,a].
la lista se ha escrito en el fichero prueba.txt
yes
La ejecucion de este u
ltimo predicado tiene como efecto colateral la escritura de los elementos
de la lista [h,o,l,a] en el fichero prueba.txt.
4.3.
Control: el corte
Los predicados de control son predicados predefinidos que permiten al programador intervenir
en el mecanismo de b
usqueda de soluciones de Prolog. En este apartado se va a introducir
exclusivamente uno de ellos, el denominado predicado de corte.
20
4.3.1.
Definici
on y propiedades
b :- ....
d.
d :- ....
e :- ....
Por otro lado, el corte tambien permite aumentar la expresividad del lenguaje: su uso, normalmente en combinacion con otros predicados predefinidos, aporta nuevas construcciones
de gran utilidad, como por ejemplo la negacion por fallo finito.
Sin embargo, el corte es un predicado muy controvertido. Al tratarse de una herramienta de
control (interviene en c
omo se debe resolver el problema), su uso entra en clara contradiccion
con los principios basicos de la programacion logica pura, que preconiza una separacion ntida
entre la logica del problema (responsabilidad del programador) y la forma de resolverlo (responsabilidad del mecanismo de demostracion automatica). As, el corte puede conducir a programas
logicos difciles de leer y de validar, y puede provocar muchos errores de programacion.
En definitiva, el predicado de corte de Prolog constituye una herramienta de caracter extralogico, muy potente pero que debe usarse con mucho cuidado y en casos muy concretos.
4.3.2.
Aunque el predicado de corte tiene usos muy variados (en general, en combinacion con otros
predicados predefinidos de Prolog) uno de los mas habituales es la simulacion de estructuras
condicionales de la forma:
si b1 entonces c1 ;
si no: si b2 , entonces c2 ;
....
si no: si bn , entonces cn ;
si no: c.
En Prolog, este tipo de estructuras, con condiciones mutuamente excluyentes, se representan
mediante un predicado definido por medio de n + 1 reglas, donde cada regla expresa una de
las posibles formas de calcular el predicado: la regla i-esima expresa que el predicado es cierto
si no se cumple ninguna de las condiciones b1 , ..., bi1 anteriores y, ademas, se cumplen las
condiciones bi y ci .
El corte permite simplificar la representacion anterior y conseguir un uso mas eficiente de este
tipo de estructuras. Su representacion utilizando el corte es la siguiente:
a :a :....
a :a :-
b1, !, c1.
b2, !, c2.
bn, !, cn.
c.
De esta forma se consigue que, en el momento en que se compruebe que se verifica una cierta
condicion bi, no se intente aplicar ninguna regla posterior.
A continuacion se describe el uso del corte mediante su aplicacion a varios ejemplos (todos ellos
contenidos en el fichero corte.pl).
23
0,
1,
f un(x) =
2,
si x 10
si 10 < x 20
si x > 20
Una primera aproximacion para la resolucion del problema anterior es definir un predicado
f(X,Y), cierto si Y es igual a f un(X), mediante las tres siguientes reglas:
f(X,0) :- X =< 10.
f(X,1) :- X>10, X =< 20.
f(X,2) :- X > 20.
La representacion anterior calcula correctamente los valores de la funcion f un, pero tiene el
siguiente inconveniente. Supongase que se realiza la consulta ?- f(0,Z), Z>1.. La respuesta
de Prolog sera no, pero para llegar a dicha conclusion el sistema tiene que recorrer las 3
posibles ramas del arbol de Resolucion SLD correspondiente (dib
ujese como ejercicio dicho
arbol). Lo anterior es poco eficiente, puesto que, al ser las tres reglas que describen el predicado
f mutuamente excluyentes, una vez que se ha encontrado una solucion con una de ellas no
tiene sentido probar con el resto. En efecto, la funcion que se esta calculando tiene la siguiente
estructura condicional:
si X 10 entonces Y = 0;
si no: si X 20, entonces Y = 1;
si no: Y = 2.
Por lo tanto, una forma de remediar la ineficiencia anterior es utilizando el predicado de corte
como se ha indicado al principio de este apartado:
f(X,Y) :- X =< 10, !, Y=0.
f(X,Y) :- X =< 20, !, Y=1.
f(_X,2).
Con esta nueva version, la respuesta de Prolog a la consulta ?- f(0,Z), Z>1. sera tambien no, pero ahora, gracias a la introduccion del corte en la primera regla, el sistema solo
tendra que explorar la primera rama del arbol SLD.
Observese que una forma mas comoda para representar esta nueva version consistira en, al
igual que en la primera version, realizar la unificacion directamente en la cabeza de las reglas:
f(X,0) :- X =< 10, !.
f(X,1) :- X =< 20, !.
f(_X,2).
24
Dado que las dos opciones son mutuamente excluyentes, una forma mas comoda y eficiente de
expresar lo anterior es utilizando el corte:
maximo(X,Y,Z) :- X >= Y, !, Z=X.
maximo(_X,Y,Y).
el predicado anterior podra utilizarse no solo para averiguar si un elemento pertenece a una
lista determinada sino tambien para recorrer todos los elementos de una lista. Por ejemplo, la
respuesta del sistema ante la consulta pertenece(X,[a,b,c]). es la siguiente (compruebese
construyendo el arbol de resolucion SLD correspondiente).
?- pertenece(X,[a,b,c]).
X = a ? ;
X = b ? ;
X = c ? ;
no
Una version mas eficiente del predicado anterior se consigue introduciendo un corte en el cuerpo
de la primera regla, de forma que el predicado termine en el momento de encontrar la primera
ocurrencia de un cierto elemento:
pertenece(C, [C|_]) :- !.
pertenece(C, [_|R]) :- pertenece(C,R).
Con esto se consigue una version determinista y mas eficiente del predicado. Sin embargo,
la introduccion del corte y la consiguiente poda del arbol de Resolucion SLD hace que el
predicado ya no se pueda usar, como con la version anterior, para enumerar todos los elementos
de una lista. En efecto, ahora se tendra (compruebese construyendo el arbol de resolucion SLD
correspondiente):
?- pertenece(X,[a,b,c]).
X = a ? ;
no
26