Estructuras incompletas. Listas diferencia. Estructuras

Anuncio
Estructuras incompletas. Listas diferencia.
1 / 13
Estructuras incompletas
Proporcionan una técnica de implementación muy potente
propia de Prolog:
Incrementan radicalmente la eficiencia de muchos programas.
Simplifican el diseño de dichos programas.
Se apoyan en un uso muy hábil de las variables lógicas para
representar agujeros o huecos en las estructuras de datos.
Estos huecos representan una parte de la estructura que aun
no ha sido computada y que se irá computanto
incrementalmente a medida que avance la ejecución.
Las estructuras incompletas pueden entenderse como una
generalización de la idea de acumulador.
La estrutura incompleta más utilizada son las listas diferencia
2 / 13
Listas revisitadas
El predicado append(?Xs,?Ys,?Zs) para concatenar dos listas se
definı́a como:
append([],Ys,Ys).
append([X|Xs],Ys,[X|Zs]):- append(Xs,Ys,Zs).
Desarrollar el árbol de búsqueda para
append([a,b,c],[d,e],Zs)
¿Qué complejidad computacional tiene esta operación? ¿Cuántas
invocaciones recursivas hace una llamada a append para
concatenar dos listas dadas?
¿Cómo podrı́a mejorarse esta complejidad?
3 / 13
Utilizando el append
Problema: definir el predicado rotacionSimple(+Xs,-Ys)::= la lista
Ys es el resultado de trasladar la cabeza de Xs al final de la misma.
Por ejemplo, si Xs=[e1 ,e2 . . . ,en ] entonces será Ys=[e2 . . . ,en ,e1 ].
rotacionSimple([],[]).
rotacionSimple([X|Xs],Ys):- append(Xs,[X],Ys).
¿Qué complejidad tiene esta operación? ¿Cómo podrı́a mejorarse?
4 / 13
Utilizando el append II
Problema: definir el predicado aplana(+Xs,-Ys)::= dada una lista
Xs con un anidamiento arbitrario de listas, Ys será la lista
resultante de aplanarla (eliminar el anidamiento de listas y obtener
una lista sin anidamiento). Por ejemplo, si
Xs=[1,[2],[],[3,[4,[]],[[[5]]]]] será Ys=[1,2,3,4,5].
aplana([],[]).
aplana([X|Xs],Ys):aplana(X,X1), !, aplana(Xs,Xs1),
append(X1,Xs1,Ys).
aplana([X|Xs],[X|Ys]):- aplana(Xs,Ys).
¿Puede mejorarse la eficiencia de esta operación?
5 / 13
Listas diferencia
La ineficiencia de los predicados anteriores es debida en gran
parte a la ineficiencia de append.
Pensando en el modo de uso: append(+Xs,+Ys,-Zs), este
predicado recorre la lista Xs completa, elemento a elemento.
En programación imperativa utilizando listas enlazadas
(mediente punteros al “siguiente”)
la correspondiente operación append puede hacerse en tiempo
constante: apuntando el último enlace de la primera lista al
principio de la segunda lista.
→ a → b → c →||
→ d → e →||
→ a → b → c → d → e →||
Pero para ello se necesita guardar la referencia al último (o
bien, puntero al último nodo, que a su vez nos da acceso al
último puntero).
6 / 13
¿Punteros en Prolog?
En Prolog la situacion es similar:
Para concatenar dos listas directamente (en tiempo
constante) se necesita una:
lista abierta, i.e., una lista con una referencia al final, que
pueda apuntarse a otra lista para hacer la concatenación
En Prolog no hay punteros... pero hay variables lógicas, que
pueden actuar como referencias. Podemos representar la lista
[a, b, c] como un par (lista abierta,referencia al resto de la
lista):
([a, b, c|R], R)
Las listas diferencia:
Siempre llevarán una variable como resto de lista.
Dicha variable debe guardarse como segundo elemento del par.
Nota: habitualmente el par ([a, b, c|R], R) se representa como
[a, b, c|R] − R o [a, b, c|R]\R (esto no es crı́tico, podemos
elegir la que más nos guste).
7 / 13
Concatenación con listas diferencia
La concatenacion de [a, b, c] y [d, e], con la nueva representación
serı́a la concatención de [a, b, c|R1] −R1 con [d, e|R2] −R2.
| {z }
| {z }
L1
L2
Se unifica R1 = L2
El nuevo resto será R2.
La lista resultante es [a, b, c, d, e|R2] −R2.
|
{z
}
L1
append con listas diferencia:
appendDif(L1-R1,L2-R2,L3-R3):- R1=L2, L3=L1, R3=R2.
O más sintético:
appendDif(A-B,B-C,A-C).
8 / 13
Manipulando listas diferencia
Importante: cuando trabajamos con listas diferencia tenemos
que manipular siempre la representación adecuada L-R en
todas los predicados/objetivos implicados.
El objetivo appendDif([1,2,3],[4,5],L) no tiene sentido porque
appendDif espera listas diferencia (de la forma L-R como
argumentos.
¿Cómo se representa la lista vacı́a como lista diferencia? L-L
Una lista diferencia puede transformarse automáticamente en
una lista estándard. Por ejemplo, dada la lista [1,2,3,4|R]-R, si
la unificamos con L-[] (es decir, unificamos R=[])
obtendremos la lista estándard L=[1,2,3,4].
La operación inversa para convertir una lista estándard en una
lista diferencia no es inmediata... ¿cómo se harı́a?
9 / 13
Rotación simple de una lista
Tenı́amos el problema: definir el predicado
rotacionSimple(+Xs,-Ys)::= la lista Ys es el resultado de trasladar
la cabeza de Xs al final de la misma. Por ejemplo, si Xs=[e1
,e2 ,. . . ,en ] entonces serı́a Ys=[e2 ,. . . ,en ,e1 ].
Utilizando listas diferencia (cambiando la representación de las
listas), tenemos:
rotacionSimple(L-L,L-L):- var(L), !. % lista vacia
rotacionSimple([X|Xs]-R,Xs-R1):- R=[X|R1].
O más sintético (haciendo la unificación en cabeza):
rotacionSimple(L-L,L-L):- var(L), !. % lista vacia
rotacionSimple([X|Xs]-[X|R1],Xs-R1).
10 / 13
Aplana revisitado
Tenı́amos:
aplana([],[]).
aplana([X|Xs],Ys):aplana(X,X1), !, aplana(Xs,Xs1),
append(X1,Xs1,Ys).
aplana([X|Xs],[X|Ys]):- aplana(Xs,Ys).
Podrı́amos utilizar listas diferencia y el append correspondiente...
pero de hecho no es necesario el append:
aplana2(Xs,Ys):- aplanaAux(Xs,Ys-[]).
aplanaAux([], L-L).
aplanaAux([X|Xs],A-D):aplanaAux(X,A-B), !, aplanaAux(Xs,C-D), B=C.
aplanaAux([X|Xs],[X|A]-B):- aplanaAux(Xs,A-B).
11 / 13
Aplana re-revisitado
Una implementación aun mejor para aplana (sin listas diferencia):
apl([],[]).
apl([[]|Xs],Ys):- !, apl(Xs,Ys).
apl([[X|Xs]|Ys],Zs):- !, apl([X,Xs|Ys],Zs).
apl([X|Xs],[X|Ys]):- apl(Xs,Ys).
12 / 13
Dos ejercicios
Implementar el predicado toDifList(Xs,Ys-Zs)::= dada la lista
(plana) Xs obtiene la correspondiente lista diferencia Ys-Zs.
Por ejemplo, toDifList([1,2,3],DL) obtendrı́a DL=[1,2,3|R]-R.
Implementar el predicado toDifListGen(T,T1)::= dado el
término T obtiene el término T1 resultante de transformar
todas las listas que tenga T como subtérmino en listas
diferencia.
Por ejemplo, toDifList([1,[2,3],[4,[5]]],DL) obtendrı́a
DL = [1, [2, 3|A]-A, [4, [5|B]-B|C]-C|D]-D
13 / 13
Descargar