Metaprogramación en Prolog - Departamento de Lenguajes y

Anuncio
Metaprogramación
Ingeniería Informática
Departamento de Lenguajes y
Ciencias de la Computación
Universidad de Málaga
Contenido
1. Clasificación de términos
2. Inspección de estructuras
Metaprogramación
2
Clasificación de términos
Clasificación de términos Prolog
Los términos Prolog se clasifican en:
átomo
constante
entero
número
real
término
variable
estructura
Metaprogramación
4
Metapredicados de clasificación
Para cada subclase de término, Prolog predefine un
metapredicado unario que nos dice si su argumento pertenece a
esa subclase:
atom(+T)
– T es
integer(+T) – T es
float(+T)
– T es
number(+T) – T es
atomic(+T) – T es
var(+T)
– T es
compound(+T)– T es
un átomo
un entero
un flotante
un número entero o flotante
una constante
una variable libre
una estructura
Todos son tests y tienen éxito si sólo si su argumento pertenece
a la subclase a la que se refieren
Metaprogramación
5
Ejemplos de clasificación de términos
?- atom(vacio).
Yes
?- integer(-3).
Yes
?- var(X).
Yes
?- compound([a,b|Xs]).
Yes
?- compound(-3.14).
No
?- integer([1]).
No
Metaprogramación
6
Clasificación de las variables instanciadas
Se clasifican respecto al término al que están instanciadas:
?- X = 2, var(X).
No
?- X = 2, integer(X).
Yes
?- var(X), X = 2.
X = 2
?- var([X]).
No
?- compound([X]).
Yes
Metaprogramación
7
El metapredicado nonvar/1
Para facilitar
metapredicado:
el
trabajo
con
variables
se
define
el
nonvar(+T) – T no es una variable libre
Ejemplos:
?- nonvar(X).
No
?- X = a, nonvar(X).
Yes
?- nonvar(vacio).
Yes
Metaprogramación
8
Aplicaciones de la clasificación de términos
En general, estos metapredicados sirven para escribir predicados
cuyo comportamiento dependa del tipo de término que reciben
En particular, pueden emplearse para:
comprobar precondiciones
diagnosticar errores de tipo
extender usos posibles
mejorar la eficiencia
distinguir casos según el tipo
Veremos algunos ejemplos
Metaprogramación
9
Ejemplo: el predicado suma/3
Consideremos el predicado suma/3:
suma(X,Y,Z) :Z is X + Y.
El siguiente objetivo da lugar a un error de ejecución:
?- suma(2,a,Z).
ERROR: is/2: Arithmetic: `a/0' is not a function
^ Exception: (8) _G256 is 2+a ?
¿Cómo podríamos evitarlo?
Metaprogramación
10
Comprobar precondiciones
Podemos comprobar el tipo de los argumentos:
suma_precond(X,Y,Z) :% precondición
number(X),
number(Y),
% precondición
Z is X + Y.
?- suma_pre(2,a,Z).
No
Evitamos el error, pero no se distingue de un fracaso:
?- suma_pre(2,3,7).
No
¿Cómo podemos informar al usuario del error de tipo?
Metaprogramación
11
Diagnosticar errores de tipo (I)
tipo(+T,?Tipo) - el término T es de tipo Tipo
tipo(T,atomo) :- atom(T).
tipo(T,entero) :- integer(T).
tipo(T,flotante) :- float(T).
tipo(T,variable) :- var(T).
tipo(T,estructura) :- compound(T).
Ejemplos:
?- tipo(2,X).
X = entero
?- tipo([a,b,c],X).
X = estructura
Metaprogramación
12
Diagnosticar errores de tipo (II)
La suma sólo se realiza si todos los tipos son correctos:
suma_tipada(X,Y,Z) :- % tipos correctos
tipo(X,TX),
esta(TX,[entero, flotante]),
tipo(Y,TY),
esta(TY,[entero, flotante]),
tipo(Z,TZ),
esta(TZ,[entero,flotante,variable]),
Z is X+Y.
Los otros casos detectan y diagnostican errores de tipo
Metaprogramación
13
Diagnosticar errores de tipo (III)
Una cláusula por cada parámetro:
suma_tipada(X,_,_) :- % error de tipo en X
tipo(X,TX),
no_esta(TX, [entero, flotante]),
write('suma(X,Y,Z):’),
write(‘X debe ser numerico y es '),
write(TX),
nl.
Ejercicio: completa la definición comprobando los tipos de Y y Z
Metaprogramación
14
Diagnosticar errores de tipo (y IV)
?- suma_tipada(2,3,X).
X = 5
?- suma_tipada(2,3,7).
No
?- suma_tipada(2,3,x).
suma(X,Y,Z): Z debe ser num o var y es atomo
?- suma_tipada(A,B,5).
suma(X,Y,Z): X debe ser numerico y es variable
suma(X,Y,Z): Y debe ser numerico y es variable
El último objetivo muestra que estamos perdiendo usos.
¿Cómo podríamos recuperar usos alternativos?
Metaprogramación
15
Extender usos posibles (I)
Una cláusula por cada uno de los usos posibles:
suma_ex(X,Y,Z) :% usos: (+,+,+) y (+,+,-)
number(X),
number(Y),
tipo(Z,TZ),
esta(TZ, [entero, flotante, variable])
Z is X+Y.
Metaprogramación
16
Extender usos posibles (y II)
suma_ex(X,Y,Z) :number(X),
var(Y),
number(Z),
Y is Z-X.
suma_ex(X,Y,Z) :var(X),
number(Y),
number(Z),
X is Z-Y.
% uso: (+,-,+)
% uso: (-,+,+)
Ejercicio: completa los otros usos de suma_ex/3
Metaprogramación
17
Mejorar la eficiencia (I)
abuelo(A,N) - A es abuelo (paterno o materno) de N
abuelo(A,N) :padre(A,P),
progenitor(P,N).
Esta definición es muy ineficiente en el uso (-,+):
?- abuelo(A,heidi).
¿Cómo podemos mejorar la eficiencia?
Metaprogramación
18
Mejorar la eficiencia (y II)
Definimos cláusulas específicas según el uso:
abuelo(A,N) :% uso: (-,+)
var(A),
atom(N),
progenitor(P,N),
padre(A,P).
abuelo(A,N) :% uso: (+,+), (+,-), (-,-)
tipo(A,TA),
tipo(N,TN),
(TA,TN) \== (variable, constante),
padre(A,P),
progenitor(P,N).
Metaprogramación
19
Distinguir casos según el tipo (I)
aplana(+Xss,?Ys) Xss lista de átomos y de listas de átomos
arbitrariamente anidadas,
Ys lista de los átomos que aparecen en Xss
Ejemplo:
?- aplana([a,[b,c],[[d,[e]],[],f],g],A).
A = [a,b,c,d,e,f,g];
No
Metaprogramación
20
Distinguir casos según el tipo (II)
aplana([],[]).
% caso base
aplana([X|Xss],Ys) :-
% caso recursivo
…
Lo que haya que hacer depende del tipo de X
X es un átomo
X es una lista
Metaprogramación
21
Distinguir casos según el tipo (y III)
aplana([],[]).
% caso base
aplana([X|Xss],[X|Ys]) :- % caso recursivo
atom(X),
X \== [],
aplana(Xss,Ys).
aplana([Xs|Xss],Ys) :% caso recursivo
es_lista(Xs),
aplana(Xs,As),
aplana(Xss,Bs),
concatena(As,Bs,Ys).
es_lista([]).
es_lista([_|_]).
Metaprogramación
22
Ejercicios
1. Define un predicado super_suma/3 que combine todas las
técnicas (tipado, diagnóstico de errores, usos múltiples)
2. Define un predicado longitud/2 que funcione correctamente
en todos los usos posibles.
3. Define una versión recursiva de cola de aplana/2. Compara
el coste con la versión original.
4. Define un predicado aplanax/2 que aplane una lista de
términos arbitrarios
Metaprogramación
23
Inspección de estructuras
Inspección de estructuras (I)
Los siguientes objetivos deberían tener éxito:
?- suma_tipada(2+3*5,1,Z).
suma(X,Y,Z): X debe ser numérico y es estructura
?- suma_tipada(3,2*5,Z).
suma(X,Y,Z): Y debe ser numérico y es estructura
?- suma_tipada(2*3,4*5,Z).
suma(X,Y,Z): X debe ser numérico y es estructura
suma(X,Y,Z): Y debe ser numérico y es estructura
Debemos aceptar la estructura como tipo de X, Y y Z
Metaprogramación
25
Inspección de estructuras (II)
suma_estruc(X,Y,Z) :- % uso (+,+,?)
tipo(X,TX),
esta(TX,[entero, flotante, estructura]),
tipo(Y,TY),
esta(TY,[entero, flotante, estructura]),
tipo(Z,TZ),
esta(TZ,[entero,flotante,estructura,variable]),
Z is X+Y.
¿Es correcto?
Metaprogramación
26
Inspección de estructuras (III)
¡No!
?- suma_estruc(1+2*3,4,Z).
Z = 11
?- suma_estruc(3+2*a,6,Z).
ERROR: is/2: Arithmetic: `a/0' is not a function
^ Exception: (8) _G328 is 3+2*a+6 ?
una estructura no es siempre una expresión aritmética válida
compound/1 no es suficiente
¿Qué necesitamos?
Metaprogramación
27
Inspección de estructuras (y IV)
Debemos inspeccionar X, Y y Z recursivamente
+
?- suma_estruc(1+2*3,4,Z).
Z = 11
1
*
2
3
?- suma_estruc(1+2*a,4,Z).
suma(X,Y,Z): X no es una expresión aritmética
+
1
*
2
Metaprogramación
a
28
Metapredicados de inspección de estructuras
Los metapredicados de inspección de estructuras permiten:
descomponer una estructura en sus componentes
componer una estructura a partir de sus componentes
Prolog predefine 3 metapredicados de inspección:
=../2
functor/3
arg/3
Metaprogramación
29
El metapredicado =../2
X =.. Y
X es cualquier término Prolog
Y es una lista cuya cabeza es el átomo del functor principal de X
y cuyo resto está formado por los argumentos de X
X =.. Y se lee X univ Y
Soporta los usos (+,+), (-,+) y (+,-)
Cuidado: el uso (-,-) genera un error:
?- A =.. B.
ERROR: Arguments are not sufficiently instantiated
Metaprogramación
30
Comprobando términos con =../2
En el uso (+,+) se comporta como un test
?- f(a, X, g(b,Y)) =.. [f, a, X , g(b,Y) ].
Yes
argumentos
átomo del
functor principal
?- [a,b,c] =.. [‘.’, a, [b,c]].
Yes
? 2+3*5-1 =.. [‘-’,2+3*5,1].
Yes
?- a =.. [a].
Yes
Metaprogramación
31
Descomponiendo términos con =../2
En el uso (+,-) se comporta como un generador único
Se utiliza para descomponer un término en sus componentes
?Xs
?Xs
?Xs
?Xs
?Xs
punto(2,3) =.. Xs.
= [punto, 2, 3]
[A,f(X),Y] =.. Xs.
= ['.', A, [f(X), Y]] ;
sin(X)*cos(X) + 3.14 =.. Xs.
= [+, sin(X)*cos(X), 3.14] ;
6 =.. Xs.
= [6]
[] =.. Xs.
= [[]]
Metaprogramación
32
Componiendo términos con =../2
En el uso (-,+) se comporta como un generador único
Se utiliza para componer un término a partir de sus componentes
?- T =.. ['+',a+b+c,d].
T = a+b+c+d
?- T =.. [arco,ej1,i,q3,q5].
T = arco(ej1, i, q3, q5)
?- T =.. ['.', p,[a,k]].
T = [p, a, k]
?- T =.. [fin].
T = fin
Metaprogramación
33
Metaprogramación con =../2
El metapredicado =../2 nos permite definir predicados que
procesen términos arbitrarios (tal como hacen algunos predicados
predefinidos, e.g. write/1, =/1, @</2,…)
Típicamente, en los predicados que procesan términos arbitrarios
distinguimos:
casos base: constantes y variables
caso recursivo: estructura
a menudo emplearemos recursión mutua
Esta capacidad nos servirá para implementar en Prolog el
polimorfismo estructural
Metaprogramación
34
Escalando figuras geométricas (I)
Representamos figuras geométricas planas por los términos:
Figura =::
|
|
|
cuadrado(lado)
rectangulo(anchura, altura)
triangulo(lado1, lado2, lado3)
circulo(radio)
donde los argumentos de las distintas figuras son números que
indican sus dimensiones
Ejemplos:
Metaprogramación
rectangulo(2,6)
circulo(3.14159)
35
Escalando figuras geométricas (II)
Define un predicado escala(+F,+K,?KF) que multiplique cada
dimensión de la figura F por el factor K, obteniendo la figura
escalada KF.
Ejemplo:
?- escala(rectangulo(3,5), 2, R).
R = rectangulo(6,10)
?- escala(circulo(6.5),0.5,C).
C = circulo(3.25)
En principio, necesitamos 4 reglas, una por cada tipo de figura
¿Es posible escribir un predicado escala/3 genérico?
Metaprogramación
36
Escalando figuras geométricas (y III)
escala(Fig,K,KFig) :Fig =.. [Tipo|Ds],
multiplica(K,Ds,KDs),
KFig =.. [Tipo|KDs].
% descomponer figura
% componer figura
multiplica(_,[],[]).
multiplica(K,[D|Ds],[KD|KDs]) :KD is K*D,
multiplica(K,Ds,KDs).
Funciona para cualquiera de las figuras anteriores
Ejercicio: ¿Qué tipo de términos pueden usarse con escala/3?
Metaprogramación
37
Detectando términos básicos
es_basico(+T) – T es un término básico (i.e. no tiene variables)
es_basico(T) :atomic(T).
es_basico(T) :compound(T),
T = [_|Args],
args_basicos(Args).
args_basicos([]).
args_basicos([A|As]) :es_basico(A),
args_basicos(As).
Metaprogramación
% caso base
% caso recursivo
% argumentos
% recursión mutua
% recursión mutua
38
Subtérminos de un término
subtérmino(?S,+T) – S es subtérmino del término básico T
subtermino(T,T).
subtermino(S,T) :compound(T),
T = [_|Args],
miembro(A,Args),
subtermino(S,A).
¿Por qué es importante que T sea básico?
Ejercicio: añade el test para comprobar que T es básico
Metaprogramación
39
El metapredicado functor/3
functor(T,F,N)
T es cualquier término Prolog
F es el átomo del functor principal de T
F es la aridad del functor principal de T
Soporta los usos (+,+,+), (+,-,-) y (-,+,+)
Los usos (+,+,-) y (+,-,+) son posibles, pero poco útiles
Cuidado: el resto de usos genera un error:
?- functor(T,F,3).
ERROR: Arguments are not sufficiently instantiated
Metaprogramación
40
Comprobando functores principales
En el uso (+,+,+) se comporta como un test
?- functor(t(X,a),t,2).
Yes
?- functor(2+3*5-1,’-’,2).
Yes
?- functor(a,a,0).
Yes
?- functor([x,y],’.’,2).
Yes
Metaprogramación
41
Obteniendo el functor principal
En el uso (+,-,-) se comporta como un generador único
Se utiliza para obtener el functor principal de un término
?- functor(punto(a,X),F,N).
F = punto
N = 2
?- functor([A,f(X),Y],F,N).
F = ‘.’
N = 2
?- functor([],F,N)
F = []
N = 0
Metaprogramación
42
Generando plantillas con functor/3
En el uso (-,+,+) se comporta como un generador único
Se utiliza para generar una plantilla de estructura
?- functor(T,punto,2).
T = punto(_,_)
?- functor(T,’+’,2).
T = _ + _
?- functor(T,’+’,4).
T = ‘+’(_,_,_,_)
?- functor(T,a,0).
T = a
La utilidad de functor/3 se aprecia al combinarlo con arg/3
Metaprogramación
43
El metapredicado arg/3
arg(+I,?T,?A)
I es un entero positivo
T es una estructura
A unifica con el argumento I-ésimo de la estructura T
Los argumentos de numeran a partir de 1, de izquierda a derecha
Soporta los usos (+,+,+), (+,+,-) y (+,-,+)
Cuidado: el resto de usos genera un error:
?- arg(3,T,A).
ERROR: Arguments are not sufficiently instantiated
Metaprogramación
44
Accediendo a argumentos por su posición
En el uso (+,+,-) se comporta como un generador único
Se utiliza para obtener el argumento i-ésimo de una estructura
?- arg(3,arco(i,q2,q0),A).
A = q0
?- arg(1,[a,b,c,d],A).
A = a
?- arg(2,[a,b,c,d],A).
A = [b, c, d]
?- arg(3,[a,b,c,d],A).
% fuera de rango
No
Metaprogramación
45
Instanciando argumentos por su posición
En el uso (+,-,+) se comporta como un generador único
Se utiliza para instanciar el argumento i-ésimo de una estructura
?- arg(2,arco(i,Q,q0),q5).
Q = q5
?- arg(1,[X,b,c,d],a).
X = a
Importante: aunque esté en modo -, el segundo argumento de
arg/3 es una estructura
Podemos metaprogramar combinando functor/3 y arg/3
Metaprogramación
46
Sustitución de términos (I)
sustituye(+X,+Y,+TX,?TY) – sustituye las apariciones de X
por Y en TX, obteniendo TY
?- sustituye(a,b,f(a,c),T).
T = f(b, c)
?- sustituye(2,-1,[1,2,3,2,5],T).
T = [1, -1, 3, -1, 5]
?- sustituye(X,Y,p(X,[A,B,[C,X]],q(2+t(x,X))),T).
T = p(Y, [A, B, [C, Y]], q(2+t(x, Y)))
Metaprogramación
47
Sustitución de términos (II)
sustituye(X,Y,TX,Y) :TX == X.
sustituye(X,_,TX,TX) :atomic(TX),
TX \== X.
sustituye(X,_,TX,TX) :var(TX),
TX \== X.
sustituye(X,Y,TX,TY) :compound(TX),
TX \== X,
functor(TX,F,N), % (+,-,-)
functor(TY,F,N), % (-,+,+)
sust_args(N,X,Y,TX,TY).
Metaprogramación
48
Sustitución de términos (y III)
Un bucle dirigido por el contador para aplicar la sustitución a
cada argumento:
sust_args(0,_,_,_,_).
sust_args(I,X,Y,TX,TY) :I > 0,
arg(I,TX,TXi), % (+,+,-)
arg(I,TY,TYi), % (+,+,-)
sustituye(X,Y,TXi,TYi), % rec. mutua
I1 is I-1,
sust_args(I1,X,Y,TX,TY).
Metaprogramación
49
Ejercicios (I)
Define los siguientes metapredicados:
1. reemplaza(+T,+I,+X,?TX) reemplaza el argumento I-ésimo
de T por X, obteniendo TX
?- reemplaza(2,f(a,b,c),w,T).
T = f(a,w,c).
2. atomos(T,A) A es un átomo del término T
?- atomos(f(a,g(b,1)),A).
A = a ;
A = b ;
No
Metaprogramación
50
Ejercicios (y II)
3. lista_atomos(T,As) A es la lista de átomos del término T
?- lista_atomos(f(a,g(b,1)),As).
As = [a, b] ;
No
Metaprogramación
51
Descargar