Acceso y manipulación de programa

clause(Head,Body): ``Head'' debe de tener el nombre de una cláusula y nos regresa el cuerpo de ésta. Los ``hechos'' (i.e., con cuerpo vacío) nos regresa ``true''

?- clause(append(X,Y,Z),L).
X = [], Y=_348, Z=_348, L=true;
X=[_780|_773], Y=_348, Z=[_780|_775], L=junta(_773,_348,_775)
Esto nos permite accesar a cláusulas que tengamos definidas dentro del programa

Asi como podemos accesar predicados con clause/2, también podemos añadir o quitar clásulas con: assert(X) y retract(X). e.g.,

?- entiendo.
no
?- assert(entiendo).
yes
?- entiendo.
yes
?- retract(entiendo).
yes
?- entiendo.
no

Otro:
a :- b, c.
d :- b, not c.
e :- c, f.
b.
c.

?- a.
yes
?- assert(f).
yes
?- e.
yes
?- retract(c).
yes
Con assert y retract podemos añadir o quitar reglas
?- retract((append(_,_,_) :- append(_,_,_))).
?- assert((append([H|T],L,[H|R]) :- append(T,L,R))).
Variantes: asserta(X), assertz(X), abolish(F/N) e.g.,
?- asserta(foo(a,b)), assertz(foo(b,c)), asserta(foo(a,c)).
?- foo(X,Y).

?- functor(Pred,append,3), clause(Pred,Cuerpo), 
   retract((Pred :- Cuerpo)).
Una forma en que podemos usar assert es para guardar soluciones:
lemma(P) :- P, asserta((P :- !)).

Torres de Hanoi:

hanoi(s(0),A,B,C,[A a B]).
hanoi(s(N),A,B,C,Movs) :-
        hanoi(N,A,C,B,Movs1),
        hanoi(N,C,B,A,Movs2),
        append(Movs1,[A a B |Movs2],Movs).

hanoi(1,A,B,C,[A a B]).
hanoi(N,A,B,C,Movs) :-
        N > 1,
        N1 is N - 1,
        lemma(hanoi(N1,A,C,B,Movs1)),
        hanoi(N1,C,B,A,Movs2),
        append(Movs1,[A a B |Movs2],Movs).

Para probarlo, primero resolver en general y luego instanciar (para aprovechar los lemmas).

hanoi(N,Discos,Movs) :-
        hanoi(N,A,B,C,Movs),
        Discos = [A,B,C].

Un ejemplo clásico es Fibonacci: el número N de Fibonacci (excepto los 2 primeros que es 1) es = $f(N-1) + f(N-2)$

fibo(1,1).
fibo(2,1).
fibo(N,F) :-
      N > 2,
      N1 is N - 1,
      fibo(N1,F1),
      N2 is N - 2,
      fibo(N2,F2),
      F is F1 + F2,
      asserta(fibo(N,F)).   % Nuevo para guardar resultados
Otra forma de hacerlo es guardar los resultados al ir haciedolo. Aumentar 2 argumentos mas para tener estos resultados parciales (de N - 1 y de N -2) Osea: ffibo(2,N,F-1,F-2,(F-1 + F-2))

ffibo(N,F) :- ffibo(2,N,1,1,F).

ffibo(M,N,F1,F2,F2) :- M >= N.
ffibo(M,N,F1,F2,F) :-
      M < N,
      M1 is M + 1,
      NF2 is F1 + F2,
      ffibo(M1,N,F2,NF2,F).

Una forma para copiar dos términos es usando assert y retract

copy(X,Y) :- asserta('$tmp'(X)), retract('$tmp'(Y)).

Un predicado util es:

repeat.
repeat :- repeat.

Por ejemplo para consultar ``manualmente'' un archivo:

consulta(Archivo) :-
      see(Archivo),
      procesa,
      seen.

Alternativamente:

consult(Archivo) :-
     open(Stream,Archivo,r),   % puede ser: r, w, a, rw, ra
     procesa(Stream),
     close(Stream).

procesa :-
    repeat,
    read(Clausula),          % read(Stream,Clausula),
    procesa(Clausula), !.

procesa(X) :- 
    end_of_file(X), !.  % end_of_file(X) :- nonvar(X),
                        %                   X = end_of_file.
procesa(Clausula) :-
    asserta(Clausula), fail.

Con assert podemos simular variables globales (aunque le quitan la parte declarativa a los programas, i.e., no usar mucho):

actualiza_var_global(Nombre,Valor) :-
     nonvar(Nombre),
     retract(var_global(Nombre,_)), !,
     asserta(var_global(Nombre,Valor)).
actualiza_var_global(Nombre,Valor) :-
     nonvar(Nombre),
     asserta(var_global(Nombre,Valor)).   % 1a vez

Gensym: crea un nuevo nombre

Antes:

name(Atomo,ListaAscci)

?- name(abc,L).
L = [97,98,99]

gensym(Prefijo,Valor) :-     % se guarda: var_global(gensym(foo),4).
    var(Valor),
    atom(Prefijo),
    obten_valor(gensym(Prefijo),N),
    N1 is N + 1,
    actualiza_var_global(gensym(Prefijo),N1),
    concatena(Prefijo,N1,Valor), !.

obten_valor(X,N) :- var_global(X,N), !.
obten_valor(X,0).

concatena(X,Y,XY) :-
    name(X,LX),
    name(Y,LY),
    append(LX,LX,LXY),
    name(XY,LXY).

emorales 2012-05-03