* PROLOG * [Documento elaborado por Jos Luis Castao Gonzlez (a.k.a. Nathan @ WOPR2K)]

Nota: Busca [[[[Probar]]]] para ver qu ejercicios hay que probar en SWI para
ver si estn bien.

< TEMA 1 >----------------------------------------------------------------->8

+ Un ejemplo simple: Divisibilidad.

  - Escribir un programa para declarar que 2 divide a 6.

    divide(2,6).

  - Sesin:

    2 divide a 6?:
      ?- divide(2,6).
      Yes
    3 divide a 12?:
      ?- divide(3,12).
      No
    Mltiplos de 2:
      ?- divide(2,X).
      X = 6
      Yes
    Divisores de 6:
      ?- divide(X,6).
      X = 2
      Yes
    Elementos X, Y tales que X divide a Y:
      ?- divide(X,Y).
      X = 2
      Y = 6
      Yes

  + Ampliamos el programa anterior, aadindole que 2 divide a 12 y que 3
    divide a 6 y a 12:

    divide(2,6).
    divide(2,12).
    divide(3,6).
    divide(3,12).

    Sesin:

      ?- divide(X, Y).
      X = 2   Y = 6;
      X = 2   Y = 12;
      X = 3   Y = 6;
      X = 3   Y = 12;
      No

    Devolver un X que sea divisible por 2 "y" por 3:
      ?- divide(2,X), divide(3,X).
      X = 6;
      X = 12;
      No

  + Ampliar el programa anterior aadindole que los nmeros divisibles por
    2 y por 3 son divisibles por 6.

    divide(2,6).
    divide(2,12).
    divide(3,6).
    divide(3,12).
    divide(6,X) :-
      divide(3,X),
      divide(2,X).

    Sesin:

      ?- divide(6,X).
      X = 6;
      X = 12;
      No

      ?- divide(X,Y).
      X = 2   Y = 6;
      X = 2   Y = 12;
      X = 3   Y = 6;
      X = 3   Y = 12;
      X = 6   Y = 6;
      X = 6   Y = 12;
      No

+ Programa leche.pl:

    es_leche :-
      parece_leche,
      lo_da_la_vaca.
    parece_leche :-
      es_blanco,
      hay_una_vaca_en_la_etiqueta.
    lo_da_la_vaca.
    es_blanco.
    hay_una_vaca_en_la_etiqueta.

    Sesin:

      ?- es_leche.
      yes

+ Los Nmeros Naturales.

   Los nmeros naturales se forman a partir de 0 y la funcin sucesor:

   nat(0).
   nat(s(X)) :- nat(X).

   Sesin:

      ?- nat(s(s(0)).
      Yes
      ?- nat(dos).
      No
      ?- nat(X).
      X = 0;
      X = s(0);
      X = s(s(0));
      X = s(s(s(0)));
      X = s(s(s(s(0))))
      Yes

      [podramos haber seguido introduciendo ';' hasta que quisiramos]

   + Definir el predicado suma(X,Y,Z) de forma que si X e Y son dos nmeros
     naturales con la representacin anterior, entonces Z es el resultado de
     sumar X e Y.

     suma(0,Y,Y).
     suma(s(X),Y,s(Z)) :- suma(X,Y,Z)).

     Sesin:

       ?- suma(s(0),s(s(0)),X).
       X = s(s(s(0)))
       Yes
       ?- suma(X,s(0),s(s(s(0)))).
       X = s(s(0))
       Yes
       ?- suma(X,Y,s(s(0))).
       X = 0       Y = s(s(0));
       X = s(0)    Y = s(0);
       X = s(s(0)) Y = 0;
       No

> EJERCICIOS <------------------------------------------------------------->8

Ejercicio 1.1: Escribir un programa que tenga como clusulas:

   1. Todo hombre es mortal.
   2. Scrates es un hombre.

   y utilizarlo para demostrar que Scrates es mortal.

   mortal(X):-hombre(X).
   hombre(Socrates).

   Sesion:

   ?- mortal(Socrates).
   Yes.

Ejercicio 1.2: Tomando la notacin 0, s(0), s(s(0)), ... para los nmeros
   naturales, dar un programa que encuentre los nmeros impares.

   impar(s(0)).
   impar(s(s(X))) :- impar(X).

Ejercicio 1.3: Aade la clusula nat(a) al final del programa naturales.pl:

   nat(0).
   nat(s(X)) :- nat(X).
   nat(a).

   .Prueba este nuevo programa que a es un nmero natural? S.
   .Pdele al programa que encuentre un nmero natural, luego otro y otro,...
    descubrir prolog que a es un nmero natural?
    No, ya que comenzar a descender por la rama infinita que crea la segunda
    regla.
   .Qu ocurre si en vez de aadirla al final la aadimos al principio?
    Entonces cuando preguntemos por naturales 'a' ser la primera respuesta,
    seguida de 0, s(0), etctera.

Ejercicio 1.4: Son unificables X y f(X)? Prueba en Prolog ?- X = f(X).
    R: Segn la lgica de Primer Orden, X y f(X) no son unificables. Prolog,
    por razones de eficiencia, no implementa ese tipo de comprobaciones que
    deberan llevar a un error, por lo que al introducir X = f(X) como pregunta
    lo dejamos 'colgado'.

Ejercicio 1.5: Consideremos la definicin del predicado padre/2:

   padre(juan, pepe).
   padre(pepe, pedro).
   padre(pedro, maria).
   padre(pedro, agustin).

   .Define el predicado abuelo/2 utilizando el predicado padre/2:

   abuelo(X,Z):- padre(X,Y), padre(Y,Z).

   .Define el predicado antecesor/2 utilizando el predicado padre/2:

   antecesor(X,Y):-padre(Y,X).
   antecesor(X,Z):-
      antecesor(X,Y),
      padre(Z,Y).

Ejercicio 1.6: Considera el siguiente programa caminos.pl

   [1]   camino(X,Z):-arco(X,Y), camino(Y,Z).
   [2]   camino(U,U).
   [3]   arco(b,c).

   .rbol de resolucin para
            ?- camino(V,c)
 X<-V, Z<-c       | [1]
                 / \
         arco(V,Y)  camino(c,c).
 V<-b       | [3]       | [2] U<-c
 Y<-c                  

   .Qu ocurrira si en el programa caminos.pl tomramos la funcin de
   seleccin que elige el ltimo literal de la clusula?
   R: Que entraramos en una rama de recursin infinita.

Ejercicio 1.7: Considera la teora que tiene como axiomas

   hermano(Y,X):-hermano(X,Y).
   hermano(cain, abel).

   .Es hermano(abel, cain) un teorema de esa teora?.
   R: No. Entramos en una recursin infinita por la primera regla.

   .Qu podemos hacer para que lo demuestre?

   hermano(cain, abel).
   hermano(Y,X):-hermano(X,Y).

[[DUDA]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
Ejercicio 1.8: [Van Le-93 p.15] Considera las siguientes afirmaciones en
   castellano:

   Toda madre ama a su hijo si su hijo es bueno.
   Toda madre es una mujer
   Ana es una mujer
   El marido de Ana es bueno.

   Vamos a trasladar ese conocimiento a Prolog, uno con smbolos de funcin
   y otro sin ellos:

   PROGRAMA 1:
   ama(madre(X), X) :- es_bueno(X).
   es_mujer(madre(X)).
   es_mujer(ana).
   es_bueno(marido(ana)).

   PROGRAMA 2:
   ama(X,Y) :- madre(X,Y), es_bueno(Y).
   es_mujer(X) :- madre(X,Y).
   es_mujer(ana).
   es_bueno(X) :- marido(X,ana).

   .Da razones de por qu el programa 1 es ms expresivo que el programa 2:
   R:

   .Escribe una cuestin para preguntar si existe alguna mujer que ame al
   marido de alguien. Cul es la respuesta de cada uno de los programas?.

   PROGRAMA (1).
   ?- es_mujer(X), marido(Y), ama(X,Y).
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

Ejercicio 1.9.
   Usando la notacin 0, s(0), s(s(0)), define p(X) que se verifique si X
   es mltiplo de 2 o de 3.

   p(X):-m2(X).
   p(X):-m3(X).
   m2(s(s(0))).
   m2(s(s(X))):-m2(X).
   m3(s(s(s(0)))).
   m3(s(s(s(X)))):-m3(X).

< TEMA 2 >----------------------------------------------------------------->8

+ Representacin de listas:

  Smbolos:
    * Una constante []
    * Un operador binario .
  Notacin de trminos:
    * []
    * .(a,[])
    * .(1,.(2,.(3,.(4,[]))))
    * .([],[])
  Notacin reducida:
    * []
    * [a]
    * [1,2,3,4]
    * [[]]

  + El predicado display:

    ?- display([]).
    []
    Yes
    ?- display([a]).
    .(a, [])
    Yes
    ?- display([1,2,3,4]).
    .(1, .(2, .(3, .(4, []))))
    Yes
    ?- display([[]].
    .([], []).

  + El predicado de unificacin =

    ?- X = .(a, .(1, [])).
    X = [a, 1]
    Yes
    ?- .(X,Y) = [1].
    X = 1
    Y = []
    Yes
    ?- .(X,Y) = [a,b,c].
    X = a
    Y = [b, c].
    ?- .(X, .(2, .(Z, []))) = [1, Y, 3].
    X = 1
    Y = 2
    Z = 3
    Yes

    Operador |: Indica que la variable que lo sigue contiene una lista
    con el resto de elementos que quedan en la lista:

    Si la lista [1,2,3,4] la expresamos como [X|Y], entonces X ser 1 e
    Y ser la lista [2,3,4].

    ?- [X|Y] = [1,2,3].
    X = 1
    Y = [2,3]
    Yes
    ?- [X,Y|Z] = [1,2,3].
    X = 1
    Y = 2
    Z = [3]
    Yes
    ?- [X,Y] = [1,2,3].
    No
    ?- [X,Y|Z] = [1,2]
    X = 1
    Y = 2
    Z = []
    Yes

    ?- [X,a,b|[]] = [[b,L],a|L]
    X = [b, [b]]
    L = [b]
    Yes
    ?- [X,Y,Z] = [Y,Z,X]
    X = _G171
    Y = _G171
    Z = _G171
    Yes           (Vemos que prolog los unifica, ya que les da la misma
                   identidad a las tres variables).
    ?- p([X|R],h(X,[a|[R|L])) = p([a],h(a,[L])).
    No            (por la variable L).
    ?- X = f(X).
                  (se queda colgado, ya que la expresin no es unificable
                   pero prolog, por eficiencia, no lo comprueba, cayendo en
                   un bucle infinito).

  + Primer elemento y Resto de la lista:

    primero(L,X) se verifica si X contiene el primer elemento de L.
    resto(L,X) se verifica si X contiene todos los elementos de L menos el
               primero.

    primero([X|L],X).
    resto([X|L],L).

    Sesin:

      ?- primero([a,b,c],X).
      X = a
      Yes
      ?- primero([X,b,c],a).
      X = a
      Yes
      ?- primero([X,Y],a).
      X = a
      Y = _G286   (no puede unificar con nada, muestra la identidad).
      Yes
      ?- primero(X,a).
      X = [a|_G353]
      ?- resto([a,b,c],L).
      L = [b, c]
      Yes
      ?- resto([a|L],[b,c]).
      L = [b, c]
      Yes

  + Aadir un elemento a la lista:

    cons(X, L1, L2) se verifica si L2 es la lista obtenida aadindole X,
    como primer elemento, a la lista L1:

    cons(X, L1, [X|L1]).

    Sesin:

      ?- cons(a,[b,c],L).
      L = [a, b, c]
      Yes
      ?- cons(X, L, [a, b, c]).
      X = a
      L = [b, c]
      Yes

  + Concatenacin de listas:

    conc(L1, L2, L3) se verifica si L3 es la lista de obtenida de concatenar
    L2 al final de L1:

    conc([],L2,L2).
    conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3).

    Sesin:

      ?- conc([a,b],[c,d,e],L).
      L = [a, b, c, d, e];
      No
      ?- conc([a,b],L,[a,b,c,d]).
      L = [c, d];
      No
      ?- conc(L1,L2,[a,b]).
      L1 = []
      L2 = [a,b];
      L1 = [a]
      L2 = [b];
      L1 = [a,b]
      L2 = [];
      No

   * Pertenece b a la lista [a,b,c]?

     ?- conc(L1,[b|L2],[a,b,c]).
     L1 = [a]
     L2 = [c];
     No
     ?- conc(_,[b|_],[a,b,c]).      <- Variables annimas.
     Yes.

   * es [b,c] una sublista de [a,b,c,d]?

     ?- conc(_,[b,c|_],[a,b,c,d]).
     Yes.

   * Cul es el ltimo elemento de [b,c,a,d]?.

     ?- conc(_,[X],[a,b,c,d]).
     X = d
     Yes

  [conc es equivalente al predicado predefinido en SWI-Prolog append(L1,L2,L3)]

+ Operadores prefijos e infijos:

  Precedencia:

    ?- display((a+b) * (5/c)).
    *(+(a, b), /(5, c))
    Yes

    ?- display(a+b*5/c).
    +(a, /(*(b, 5), c))
    Yes

    [ ^ ] -> [ *, / ] -> [ - ] -> [ +, - ].

    ^       Infijo, asociativo por la derecha.
    *,/,+,- Infijos, asociativos por la izquierda.
    -       Prefijo.

  ?- X is 2+3^3
  X = 29                   (is evala la expresin aritmtica)
  Yes
  ?- 29 is X+3^3
  [WARNING: Arguments are not sufficiently instantiated]
  ?- X = 2+3^3
  X = 2+3^3                (= slamente unifica ambas expresiones)
  Yes
  ?- 2+3^Y = 2+3^3
  Y = 3
  Yes
  ?- 3=<5.
  Yes
  ?- 3>X.
  [WARNING: Arguments are not sufficiently instantiated]
  ?- 2+5 = 10-3.
  No                       (Ya que no puede unificar)
  ?- 2+5 =:= 10-3          
  Yes                      (=:= evala ambas expresiones, Yes si iguales)
  ?- 2+5 =\= 10-3
  No                       (=\= evala ambas expresiones, Yes si distintos)
  ?- 2+5 =\= 10^3
  Yes

  + Mximo de dos nmeros

    maximo(X,Y,Z) se verifica si Z es el mximo de los nmeros X e Y.

    maximo(X,Y,X) :- X >= Y.
    maximo(X,Y,Y) :- X < Y.

    Sesin:

      ?- maximo(2,3,X).
      X = 3;
      No
      ?- maximo(3,2,X).
      X = 3;
      No

  + Factorial de un nmero

    factorial(X,Y) se verifica si Y es X!.

    factorial(0,1).
    factorial(X,Y) :-
      X1 is X - 1,
      factorial(X1,Y1)
      Y is Y1 * X.

    Sesin:

      ?- factorial(4,Y).
      Y = 24
      Yes

  + Sucesin de fibonacci

    fibonacci(N,X) se verifica si X es el N-simo trmino de la sucesin
    de Fibonacci.

    fibonacci(0,0).
    fibonacci(1,1).
    fibonacci(N,X) :-
      N > 1,              % Si N no es mayor que 1, se para.
      N1 is N-1,
      fibonacci(N1,X1),
      N2 is N-2,
      fibonacci(N2,X2),
      X is X1+X2.

    Sesin:

      ?- fibonacci(6,X).
      X = 8
      Yes

+ Listas y aritmtica

  + Longitud de una lista:

    longitud(L,N) se verifica si N es el nmero de trminos de la lista L.

    longitud([],0).
    longitud([_|L],X) :-
      longitud(L,X1),
      X is X1+1.

    Sesin:

      ?- longitud([a,b,c],N).
      N = 3
      Yes

  [longitud es eq. al predicado predefinido en SWI-Prolog lenght(L,N)]

  + Mximo de una lista:

    max_list(L,N) se verifica si N es el mximo de los elementos de la lista L.

    [[[[Probar]]]]--------------------------------------------------------->8

    max_list([X],X).
    max_list([X,Y|L],Z):-
      max_list([Y|L],Z1),    % Utilizamos Y para exigir que haya al menos
      X>Z1,                  % 1 elto. en la lista
      Z is X.

    ----------------------------------------------------------------------->8

    o bien:

    max_list([X],X).
    max_list([X,Y|L],Z):-
      max_list([Y|L],U),
      maximo(X,Y,Z).
    maximo(X,Y,X) :- X >= Y.
    maximo(X,Y,Y) :- X < Y.

   + Entre:

     entre(N1,N2,X) se verifica si X es mayor o igual que N1 y menos o igual
     que N2.

     entre(N1,N2,N1).      % Caso base cuando X = N1
     entre(N1,N2,X) :-
       N1 < N2,
       N3 is N1 + 1,
       entre(N3,N2,X).

  [entre es eq. al predicado predefinido en SWI-Prolog between(N1,N2,X)]

+ Operadores definidos por el usuario:

  pedro come pan ==> come(pedro, pan).
  juan tiene discos ==> tiene(juan, discos).

  + Definicin de operadores, precedencia:

  :- op(600,xfx,come).

  Donde:

    + 600 es la precedencia, que se expresa con un nmero de 1 a 1200.
    + xfx es el tipo de operacin (infijo, postfijo) y su asociatividad.
      Los tipos son:

      fx, fy:     prefijo
      xfx:        infijo no asociativo.
      yfx:        infijo asociativo por la izquierda.
      xfy:        infijo asociativo por la derecha.
      xf, yf:     postfijo

   La precedencia y el tipo de asociatividad de los operadores predefinidos
   se ve en la siguiente tabla:

   Operadores:       Tipo:       Precedencia:

   <, <=, >, >=,
   =:=, =/=,         xfx         700
   +, -              yfx         500
   -                 fx          500
   *, /              yfx         400
   ^                 xfy         200

   + Estructura de rbol:

   juan y pedro comen pan:

               comen
              /     \
             y       pan
            / \
        juan   pedro

   - Sin operadores:

     comen(y(juan, pedro), pan).

   - Con operadores:

     :-op(800,xfx,comen).
     :-op(400,xfx,y).
     juan y pedro comen pan.

     % Con la asociatividad eliminamos la notacin con parntesis,
     % y mediante la precedencia nos aseguramos de que "juan y pedro"
     % se evale antes que "pedro comen pan".

     Sesin:

       ?- display(juan y pedro comen pan).
       comen(y(juan, pedro), pan).
       Yes
       ?- Quienes comen pan            (Ntese: las variables en [M]aysculas)
       Quienes = juan y pedro;
       No
       ?- Alguien y pedro comen pan.
       Alguien = juan;
       No
       ?- juan y pedro comen Algo.
       Algo = pan;
       No

  + Precedencia entre argumentos:

    La precedencia de un trmino simple es cero.
    La de uno compuesto es la del smbolo de funcin principal (el ms
    extero) o cero si no est definido.

  + x e y, en la notacin fx, xfx, etc...:
    x representa al argumento cuya preferencia es menor que la del operador.
    y representa al argumento cuya preferencia es mayor o igual que la del op.

  + Ejemplo:

    El libro de ciencias de Juan es rojo:

                                               es
                                              /  \
                                            de    rojo
                                           /  \
                                         de    juan
                                        /  \
                                el_libro    ciencias

   :-op(800,xfx,es).
   :-op(400,yfx,de).    % As se asocia primero el argumento de la izquierda.
   el_libro de ciencias de juan es rojo.

   Sesin:

     ?- display(el_libro de ciencias de juan es rojo).
     es(de(de(el_libro, ciencias), juan), rojo)
     Yes
     ?- display(X es rojo).
     es(_G177, rojo)          % Aparece _G177 pq X an no ha sido unificado
     X = _G177                % con ningn trmino.
     Yes
     ?- X es rojo.                      % [*]
     X = el_libro de ciencias de juan     
     Yes
     ?- display(X de Y es rojo).
     es(de(X, Y), rojo).           
     Yes
     ?- X de Y es rojo,
     X = el_libro de ciencias
     Y = rojo
     Yes
     ?- display(el_libro de X es rojo).
     es(de(el_libro, X), rojo).
     Yes
     ?- el_libro de X es rojo
     No                          % Pq. X = el_libro de ciencias de juan, v.[*]

> EJERCICIOS <------------------------------------------------------------->8

Ejercicio 2.1. Dibujar el rbol representado por las siguientes expresiones:

   .lista(lista(a,nil),lista(b,lista(c,nil)))

                     lista
                    /     \
               lista       lista
              /     \     /     \
             a      nil  b       lista
                                /     \
                               c       nil

   .a+3-4

                       +
                      / \
                     a   -
                        / \
                       3   4

   .3^a-b
                       ^
                      / \
                     3   -
                        / \
                       a   b

Ejercicio 2.2. [Flach-94 p.47] Dado el programa

   lista(nil).
   lista(cons(_,Y)) :- lista(Y).

   a(c).
   a(X):- b(X).

   dibujar el rbol SLD correspondiente a la pregunta ?- lista(L)

               ?- lista(L)
                 [1]/  \[2] 
       L<-nil      /    \     L<-cons(X,Y)
                        lista(Y)
                        [1]/  \[2]
                  Y<-nil  /     \   Y<-cons(X',Y')
                                lista(Y')
                                [1]/  \[2]
                         Y'<-nil  /    \
                                       ...

Ejercicio 2.3. son unificables L y [a|L]?
   R: No, por las normas de la unificacin.
   [a|L]=.(a,L) . funcion
   L=.(a,L) ==> X = f(X) lo que es un peaso de error y se kea el prolog kolgao

Ejercicio 2.4. Escribir definiciones para los siguientes predicados usando, si
   se considera oportuno, los predicados ya definidos.

   (el predicado conc):

   conc([],L2,L2).
   conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3).

   1. pertenece_1(X,L) si X es un elemento de la lista L (usando conc):

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

   2. pertenece_2(X,L) si X es un elemento de la lista L (sin usar conc).

   pertenece_2(X,[X|_]).
   pertenece_2(X,[Y|L]):-pertenece(X,L).

   3. inversa(L1,L2) si L2 es la lista L1 en orden inverso.

   inversa([],[]).
   inversa([X|L],L2) :- conc(L3,[X],L2), inversa(L,L3).

   4. ultimo_1(X,L) si X es el ltimo elemento de la lista L (usando conc):

   ultimo_1(X,L) :- conc(_,[X],L).

   5. ultimo_2(X,L) si X es el ltimo elemento de la lista L (sin usar conc):

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

   6. borrado_1(X,L1,L2) si al borrar una ocurrencia del elemento X de la
   lista L1 obtenemos la lista L2 (sin usar conc):

   borrado_1(X,[X|L],L).
   borrado_1(X,[Y|L1],[Y|L2]):-borrado_1(X,L1,L2).

   7. borrado_2(X,L1,L2) como borrado_1 pero usando conc.

   borrado_2(X,L1,L2):-conc(LA,[X|LB],L1), conc(LA,LB,L2).

   8. pertenece_con_borrado(X,L), anlogo a pertenece_1 pero usando borrado_1.

   pertenece_con_borrado(X,L):-borrado_1(X,L,_).

   9. sublista(L1,L2) si L1 es una sublista de L2.

   sublista(L1,L2):- conc(_,L3,L2), conc(L1,_,L3).

   10. palindromo(L) si L es un palndromo.

   elimina_ultimo([X],[]).
   elimina_ultimo([X|L1],[X|L2]):-elimina_ultimo(L1,L2).

   palindromo([]).
   palindromo([X]).
   palindromo([X|L]):- ultimo_2(X,L), elimina_ultimo(L,L2), palindromo(L2).

Ejercicio 2.5. [Bratko-86 p.91] (Algoritmo de Euclides). Dados dos enteros
   positivos X e Y, el mximo comn divisor (m.c.d.) D puede obtenerse de la
   siguiente manera:

   .Si X e Y son iguales, entonces D es igual a X.
   .Si X<Y, entonces D es igual al mximo comn divisor de X y la diferencia
    Y-X.
   .Si Y<X entonces haremos lo mismo que en caso anterior con X e Y intercam-
    biados.

   Define el predicado mcd(X,Y,D) que calcule el m.c.d. D de los positivos X
   e Y.

   mcd(X,X,X).
   mcd(X,Y,D):-
      X>0,
      Y>0,
      X<Y,
      Y1 is Y-X,
      mcd(X,Y1,D).
   mcd(X,Y,D):-
      X>0,
      Y>0,
      Y<X,
      X1 is X-Y,
      mcd(X1,Y,D).

Ejercicio 2.6. [Flach-94 p.62] Definir el predicado cero(A,B,C,X) de forma
   que, dados los coeficientes A,B y C calcule ambos valores de X para los
   cuales A*X^2+B*X+C = 0. Nota: sqrt(X) es la raz cuadrada de X.

   Por ejemplo:
   ?-cero(1,-5,6,X).
   X = 3; X = 2; No.

   cero(A,B,C,X):-
      X1 is (-B^2 + sqrt(-4 * A * C)) / (2*A),
      X is X1.
   cero(A,B,C,X):-
      X1 is (-B^2 - sqrt(-4 * A * C)) / (2*A),
      X is X1.                                  [*** CONSULTAR ***]

Ejercicio 2.7. Se considera el siguiente programa que define el predicado
   binario m.

   m([],[]).
   m([X|L1],[X|L2]) :- m(L1,L2).
   m(L1,[_|L2]) :-     m(L1,L2).

   1. Escribir los rboles de resolucin SLD y las respuestas correspondientes
   al programa y a las siguientes preguntas:

   .                            ?-m([a,Y],[a,b]).
    X<-A,L1<-[Y],L2<-[b]  [2] /                  \[3] L1<-[a,Y]
                             /                    \   L2<-[b]
                      m([Y],[b])                   m([a,Y],b)
   Y<-b,X<-Y  [2]    /                                 | [3]  L1<-[a,Y]
                m([],[])                               |      L2<-[]
                    | [1]                          m([a,Y],[])
                                                      No.

   Salida: Y = b; No.

   .                            ?-m([Y,a],[a,b]).
   Y<-a,L1<-[a],L2<-[b]   [2] /                   \ [3] L1<-[Y,a]
                             /                     \    L2<-[b]
                       m([a],[b])             m([Y,a],[b]).  
 L1<-[a],L2<-[]   [3] /             Y<-b      [2]  / \ [3] L1<-[Y,a]
                 m([a],[])        L1<-[a],L2<-[]  /   \    L2<-[]
                    No.                    m([a],[])   m([Y,a],[]) 
                                              No.         No.

   Salida: No.

   2. Explicar qu relacin existe entre L1 y L2 para que se verifique
   m(L1,L2)-> L1 debe ser una subconjunto ordenado de L2.

Ejercicio 2.8. [Flach-94 p.63] Dado el programa:

   longitud([],0).
   longitud([X|R], N) :- longitud(R,M), N is M+1.

   dibujar el rbol SLD correspondiente a la pregunta

                                ?- longitud([a,b,c],N).
                                            |  X<-a, R<-[b,c]
                                           / \
                         longitud([b,c],N1)   N is 2+1
                               | X<-b, R<-[c]      |
                              / \                   N = 3
              longitud([c],N2]   N1 is 1+1
                  | X<-c, R<-[]
        M2<-0    / \     
  longitud([],0)    N2 is 0+1

  Resultado: N = 3; No.

Ejercicio 2.9. [Flach-94 p.63] Dado el programa:

   longitud(L,N):-
      longitud_ac(L,0,N).

   longitud_ac([],N,N).
   longitud_ac([X|R],N0,N):-
      N1 is N0+1,
      longitud_ac(R,N1,N).

   Dibujar el rbol SLD correspondiente a la pregunta ?- longitud([a,b,c],N).

   [<PASO>].

Ejercicio 2.10. Considera el programa:

   :- op(500,yfx,a).
   b a c a l a o.

   .Qu responde Prolog a las siguientes preguntas?.

   yfx significa que 'a' es asociativo por la izquierda. El hecho que
   tenemos definido sera, pues:

   a(a(a(b,c),l),o).

   - M a l a S

   o sea, a(a(M,l),S).

   M = b a c,         (o sea, a(b, c).)
   S = o.

   - b a c a S

   o sea, a(a(b,c),S).

   No.

< TEMA 3 >----< ESTRUCTURAS >---------------------------------------------->8

+ Introduccin: Segmentos verticales y horizontales. Objetos estructurados.

  + Representacin:
    punto(X,Y)
    seg(P1,P2)
  + horizontal(S) se verifica si el segmento es horizontal.
  + vertical(S) se verifica si el segmento es vertical.

    vertical(seg(punto(1,2),punto(1,3))) => S
    vertical(seg(punto(1,2),punto(4,2))) => No
    horizontal(seg(punto(1,2),punto(1,3))) => No
    horizontal(seg(punto(1,2),punto(4,2))) => S

  + Programa: ver_hor.pl:

    horizontal(seg(punto(X1,Y), punto(X2,Y))).
    vertical(seg(punto(X,Y1), punto(X,Y2))).

  Sesin:

   ?- vertical(seg(punto(1,1),punto(1,2))).
   Yes
   ?- vertical(seg(punto(1,1),punto(2,2))).
   No
   ?- vertical(seg(punto(1,1),punto(2,Y))).
   No
   ?- vertical(seg(punto(1,2),punto(X,3))).
   X = 1;
   No

   Para qu puntos el segmento que comienza en (2,3) es vertical?
   ?- vertical(seg(punto(2,3),P)).
   P = punto(2, _G459);         
   No

   Hay algn segmento que sea horizontal y vertical?
   ?- vertical(S), horizontal(S).
   S = seg(punto(_G444,_G445),
           punto(_G444,_G445));
   No
   ?- vertical(_), horizontal(_).
   Yes

  + no_degenerado(S) se verifica si S es un segmento no degenerado (que no
    se reduce a un punto):

    no_degenerado(seg(P1,P2)) :- P1 =\= P2.

  Sesin:

   Todos los segmentos que son verticales y horizontales son degenerados?
   ?- vertical(S), horizontal(S), no_degenerado(S).
   No

+ Recuperacin de la informacin: Uso de estructuras.

  Familias: familia(padre,madre,[hijos]).
            persona([nombre],nacimiento,ocupacin).

  * Descripcin de la familia 1.

    + el padre: Toms Garca Prez, nacido 7-mayo-1950. Profesor, 10.000 pts
      diarias.
    + la madre: Ana Lpez Ruiz, nacida 10-marzo-1952. Mdica, 15.000 pts
      diarias.
    + hijo: Juan Garca Lpez, nace 5-Enero-1970. Estudiante.
    + hija: Mara Garca Lpez, nace 12-Abril-1972. Estudiante.

    familia(
      persona([tomas,garca,prez],
              fecha(7,mayo,1950),
              trabajo(profesor,10000)),
      persona([ana,lpez,ruiz],
              fecha(10,marzo,1952),
              trabajo(mdica,15000)),
      [ persona([juan,garca,lpez],
                fecha(5,enero,1970),
                estudiante),
        persona([mara,garca,lpez],
                fecha(12,abril,1972),
                estudiante) ]).

    Descripcin de la familia 2.

    + el padre: Jos Prez Ruiz, nace 6-Marzo-1953. Pintor, 20.000 pts
    + la madre: Luisa Glvez Prez, nace 12-Mayo-1954. Mdica, 15.000 pts
    + hijo: Juan Luis Prez Prez, nace 5-Febrero-1980. Estudiante.
    + hija: Mara Jos Prez Prez, nace 12-Junio-1982. Estudiante.
    + hijo: Jos Mara Prez Prez, nace 12-Julio-1984. Estudiante.

    familia(
      persona([jos,prez,ruiz],
              fecha(6,marzo,1953),
              trabajo(pintor,20000)),
      persona([luisa,glvez,prez],
              fecha(12,mayo,1954),
              trabajo(mdica,15000)),
      [ persona([juan_luis,prez,prez],
                fecha(5,febrero,1980),
                estudiante),
        persona([mara_jos,prez,pres],
                fecha(12,junio,1982),
                estudiante),
        persona([jos_mara,prez,prez],
                fecha(12,julio,1984),
                estudiante) ]).

   Sesin.

     Existe alguna familia sin hijos?
     ?- familia(_,_,[]).
     No

     Existe alguna familia con un hijo?
     ?_ familia(_,_,[_]).
     No

     Existe alguna familia con dos hijos?
     ?_ familia(_,_,[_,_]).
     Yes

     Existe alguna familia con tres hijos?
     ?_ familia(_,_,[_,_,_]).
     Yes

     Existe alguna familia con cuatro hijos?
     ?_ familia(_,_,[_,_,_,_]).
     No

     Buscar los nombres de los padres de familia con tres hijos.
     ?_ persona(NP,_,_), familia(NP,_,[_,_,_]).
     NP = [jose, perez, ruiz];
     No

   + Hombres casados:

     casado(X) se verifica si X es un hombre casado:

     casado(X) :- familia(X,_,_).

     Sesin:

     ?- casado(X).
     X = persona([tomas, garcia, perez],
                 fecha(7, mayo, 1950),
                 trabajo(profesor, 10000));
     X = persona([jose, perez, ruiz],
                 fecha(6, marzo, 1953),
                 trabajo(pintor, 20000));
     No

   + Mujeres casadas:

     casada(X) se verifica si X es una mujer casada:

     casada(X) :- familia(_,X,_).

     Sesin:

     ?- casada(X).
     X = persona([ana, lopez, ruiz],
                 fecha(10, marzo, 1952),
                 trabajo(medica, 15000));
     X = persona([luisa, galvez, perez],
                 fecha(12, mayo, 1954),
                 trabajo(medica, 15000));
     No

     Nombre de las mujeres casadas que trabajan:
     ?- casada(persona(N,_,trabajo(_,_))).
     N = ana;
     N = luisa;
     No

> EJERCICIOS <------------------------------------------------------------->8

Ejercicio 3.1. Podemos definir el factorial de la siguiente manera:

   f(0,1).
   f(N,X):-
      N>0,
      N1 is N-1,
      f(N1,X1),
      X is X1*N.

   .Qu responde Prolog a la pregunta ?- f(3,X).?

    X = 6.

    .Y a las preguntas ...?

    a) ?- f(N,6).
    N = 3.

    b) ?- f(3,X), f(N,X).

    X = 6
    N = 3.

    .Define suma/3 y multiplica/3 basados en la aritmtica del sucesor.

    suma(Sumando1, Sumando2, Resultado):

    suma(0,X,X).
    suma(s(X),Y,s(R)) :- suma(X,Y,R).

    multiplica(0,X,0).
    multiplica(s(X),Y,R) :-
      multiplica(X,Y,R1),
      suma(R1,Y,R).

   .Define fac_suc(N,X) que se verifique si X es el factorial de N con la
   aritmtica del sucesor.

   fac_suc(0,s(0))
   fac_suc(s(N),X):-
      fac_suc(N,X1),
      multiplica(s(N),X1,X).

Ejercicio 3.5. Definir el predicado producto_cartesiano(L1, L2, Producto) tal que
   tome como entradas las listas L1 y L2 y devuelva la lista Producto formada
   por todos los pares ordenados del producto cartesiano de L1 y L2. Cada par
   se representar como una lista de dos elementos. Por ejemplo:

   ?- producto_cartesiano([1,2,3],[a,b],L).
      L=[[1,a],[1,b],[2,a],[2,b],[3,a],[3,b]]; No.

      conc([],L2,L2).
      conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3).

      producto_cartesiano([X],[Y],[[X,Y]]).

      producto_cartesiano([X],[Y|L1],L4):-
         producto_cartesiano([X],[Y],L2),
         producto_cartesiano([X],L1,L3),
         conc(L2,L3,L4).

      producto_cartesiano([X|L1],Y,L4):-
         producto_cartesiano([X],Y,L2),
         producto_cartesiano(L1,Y,L3),
         conc(L2,L3,L4).

Ejercicio 3.6. Considera el siguiente programa:

   misterio(X,N,L):-
      length(L,N),
      append([X],L1,L),
      append(L2,[X],L),
      L1 = L2.

   donde append(L1,L2,L3) se verifica si L3 es la lista que se obtiene de
   concatenar las listas L1 y L2 y length(L,N) si N es un entero y L es una
   lista de longitud N.

   1. Si X es un trmino cualquiera y N es un entero positivo que se
      suministran como datos de entrada, qu se obtiene como salida L?

      [una lista de longitud de N compuesta por X's]

   2. Escribe un programa recursivo que realice la misma funcin que misterio
      que no utilice predicados auxiliares y que responda "No" al pedir una
      segunda respuesta. (Las operaciones y comparaciones aritmticas estn
      permitidas).
   
      m(X,0,[]).
      m(X,N,[X|L]):-
         N>0,
         N1 is N-1,
         m(X,N1,L).

Ejercicio 3.7. [Ex.Feb. CI-98] Denominaremos sucesin de Lanford a la lista
   de longitud 27 en la cual aparecen 3 veces cada uno de los dgitos del 1
   al 9 y que adems cumple la propiedad de que entre dos "1" siempre hay
   un dgito, entre dos "2" hay dos dgitos, entre dos "3" hay tres dgitos,
   etc.

   Se pide el predicado lanford(L) que devuelva la sucesin de Lanford.
   Ejemplo de uso:

   ?- lanford(L).
      L=[1,9,1,2,1,8,2,4,6,2,7,9,4,5,8,6,3,4,7,5,3,9,6,8,3,5,7].

   lanford(L):-
      length(L)=

< TEMA 4 >----< RETROCESO, CORTE Y NEGACIN >------------------------------>8

+ Control del retroceso

   + Procedimiento sin corte: f(X,Y)

      .Definir la relacin f(X,Y) de forma que:
         - si X<3 entonces Y=0.
         - si 3 =< X, entonces Y=2.
         - si 6 =< X, entonces Y=4.

      .Programa
         f(X,0) :- X<3.
         f(X,2) :- 3=<X, X<6.
         f(X,4) :- 6=<X.

      .Traza de ?- f(1,Y), 2<Y.

         ?- f(1,Y), 2<Y.
         T Call: ( 8) f(1,_G266)
         T Call: ( 9) 1 < 3
         T Exit: ( 9) 1 < 3
         T Exit: ( 8) f(1, 0)
         T Call: ( 8) 2 < 0
         T Fail: ( 8) 2 < 0
         T Redo: ( 8) f(1,_G266)
         T Redo: ( 8) f(1,_G266)
         T Fail: ( 8) f(1,_G266)
         No

   + rbol de resolucin SLD

                           ?- f(1,Y), 2<Y.
                                  / | \
                                 /  |  \
                                /   |   \
                               /    |    \
                  [X/1] [Y/0] / [X/1]     \ [X/1] [Y/4]
                             /  [Y/2]      \
                            /       |       \
                           /        |        \
                          /         |         \
               ?- 1<3,2<0.    ?-3=<1,1<6,2<2.   ?-6=<1,2<4.
                    |               |                |
                  ?-2<0.          Fallo            Fallo
                    |
                  Fallo

   + Procedimiento con corte: f_1(X,Y)

      . Definir la relacin f_1(X,Y) a partir de la definicin de f(X,Y),
        introduciendo un corte al final de las dos primeras clusulas.

      . Programa:
         f(X,0) :- X<3, !.
         f(X,2) :- 3=<X, X<6, !.
         f(X,4) :- 6=<X.

      . Traza de ?- f_1(1,Y), 2 < Y.
         T Call: ( 8) f_1(1, _G278)
         T Call: ( 9) 1<3
         T Exit: ( 9) 1<3
         T Exit: ( 8) f_1(1,0)
         T Call: ( 8) 2<0
         T Fail: ( 8) 2<0
         No

        (Cuando PROLOG encuentra un corte, automticamente elimina todas
        las dems ramas del rbol de decisin):

                           ?- f_1(1,Y), 2<Y.
                                  / | \
                                 /  |  \
                                /   |   \
                               /    |    \
                  [X/1] [Y/0] /     |     \ 
                             /      |      \
                            /       |       \
                           /        |        \
                          /         |         \
               ?- 1<3,!,2<0.        X          X
                    |                         
                 ?-!,2<0.---------------------
                    |
                  ?-2<0
                    |
                  Fallo

   + Comentarios sobre el corte en f_1:
      .Ha mejorado la eficiencia.
      .Ha modificado la semntica procedimental.
      .No ha modificado la semntica declarativa.
   + Efectos del corte:
      .Fija todas las sustituciones de variables que se hayan realizado a
       partir de la clusula donde ha aparecido el corte.
      .No intenta encontrar soluciones alternativas a los literales a la
       izquierda del corte.
      .No intenta clusulas alternativas a la del corte.

   + Procedimiento con corte f_2(X,Y)
      .Definir la relacin f_2(X,Y) suprimiendo en la definicin de f_1(X,Y)
       las comparaciones innecesarias.

      .Programa
       f_2(X,0):-X<3,!.  <- No hay conflictos, pq si X<3 entra por aqu y !.
       f_2(X,2):-X<6,!.  <- Idem, si llega aqu es que X>=3, si entra corta. 
       f_2(X,4).         <- Si llegamos aqu es que X>=6.

      .rbol de resolucin SLD para ?- f_2(7,Y).

                               ?- f_2(7, Y)
                                  / | \
                                 /  |  \
                                /   |   \
                               /    |    \
                  [X/7] [Y/0] / [X/7]     \ [X/7] [Y/4]
                             /  [Y/2]      \
                            /       |       \
                           /        |        \
                          /         |         \
                     ?-7<3, !.   ?-7<6, !.     
                         |          |
                       Fallo      Fallo

      .rbol de resolucin SLD para ?- f_2(1,Y).

      (Vemos que con nuestro anterior diseo se habran hecho muchas evalua-
      ciones innecesarias)

                               ?- f_2(1, Y)
                                  / | \
                                 /  |  \
                                /   |   \
                               /    |    \
                  [X/1] [Y/0] / [X/1]     \ [X/1] [Y/4]
                             /  [Y/2]      \
                            /       |       \
                           /        |        \
                          /         |         \
                     ?-1<3, !.      X          X
                         |                    
                        ?-! ------------------
                         |
                         
         

      .Utilizando el mismo programa sin corte obtenemos resultados no desea-
       dos:

       f_3(X,0):-X<3.
       f_3(X,2):-X<6.
       f_3(X,4).

       ?- f_3(1,Y).
       Y = 0; Y = 2; Y = 4; No

                               ?- f_2(1, Y)
                                  / | \
                                 /  |  \
                                /   |   \
                               /    |    \
                  [X/1] [Y/0] / [X/1]     \ [X/1] [Y/4]
                             /  [Y/2]      \
                            /       |       \
                           /        |        \
                          /         |         \
                       ?-1<3      ?-1<6        
                         |          |
                                   


+ Ejemplos con corte.

   + Clculo del mximo.

      .Sin corte:
         maximo_1(X,Y,X):- Y=<X.
         maximo_1(X,Y,Y):- X=<Y.
      .Con corte:
         maximo_2(X,Y,X):-Y=<X,!.
         maximo_1(X,Y,Y).
      .Sesin:
         ?-maximo_1(3,5,X).
         X=5;
         No
         ?-maximo_2(3,5,X).
         X=5;
         No
         ?-maximo_1(3,2,2).
         No
         ?-maximo_2(3,2,2).
         Yes
      .Comentario: Vemos que aunque ganamos eficiencia podemos perder
       semntica.

   + Test de Permanencia:
      .Sin corte:
         pertenece_1(X,[X|_]).
         pertenece_1(X,[_|L]):- pertenece_1(X,L).
      .Con corte:
         pertenece_1(X,[X|_]):- !.
         pertenece_1(X,[_|L]):- pertenece_1(X,L).
      .Sesin:
         ?-pertenece_1(a,[a,b,a]).
         Yes
         ?-pertenece_1(c,[a,b,a]).
         No
         ?-pertenece_1(X,[a,b,a]).
         X = a;
         X = b;
         X = c;
         No
         ?-pertenece_2(a,[a,b,a]).
         Yes
         ?-pertenece_2(c,[a,b,a]).
         No
         ?-pertenece_2(X,[a,b,a]).
         X = a; No

      .Eq. a member y memberchk

   + Agregacin sin repeticiones

      .agregar(X,L,L1) se verifica si X es un elemento de L y L1 es L; o,
       en caso contrario, L1 es la lista obtenida aadindole X a L.

      .Ejemplos
         ?- agregar(a,[b,c],L).
         L = [a,b,c].

         ?- agregar(b,[b,c],L).
         L = [b,c].

       .Programa
         agregar(X,L,L):-pertenece(X,L),!.
         egregar(X,L,[X|L]).

+ Negacin como fallo

   + Introduccin de la negacin como fallo.

      .Programa 1
         q1(a) :- q1(b), !, q1(c).
         q1(a) :- q1(d).
         q1(d).

      .Traza
         ?- q1(a).
            Call: ( 7) q1(a) ?
            Call: ( 8) q1(b) ?
            Fail: ( 8) q1(b) ?
            Redo: ( 7) q1(a) ?
            Call: ( 8) q1(d) ?
            Exit: ( 8) q1(d) ?
            Exit: ( 7) q1(a) ?
         Yes

      . Programa 2
         q2(a) :- q2(b), !, q2(c).
         q2(a) :- q2(d).
         q2(d).
         q2(b).

         ?- q2(a).
            Call: ( 7) q2(a) ?
            Call: ( 8) q2(b) ?
            Exit: ( 8) q2(b) ?
            Call: ( 8) q2(c) ?
            Fail: ( 8) q2(c) ?
            Fail: ( 7) q2(a) ?
         No

       . Comentario: No monotona.

   ????? - En cuanto logre entenderlo lo aado.

> EJERCICIOS <------------------------------------------------------------->8

Ejercicio 4.1. [Bratko-86 p.132] Definir la relacin diferencia(C1,C2,C3) que
   se verifique si C3 es la diferencia de los conjuntos C1 y C2. Por ejemplo,
   ?- diferencia([a,b], [b,c], X)
      X = [a]; No.

   * Versin con corte:
      pertenece(X,[X|_]).
      pertenece(X,[_|L]):-pertenece(X,L).

      diferencia_1([],_,[]):-!.

      diferencia_1([X|L1],L2,L3):-
         pertenece(X,L2),
         diferencia_1(L1,L2,L3),
         !.

      diferencia_1([X|L1],L2,[X|L3]):-
         diferencia_1(L1,L2,L3).
      
   * Versin con Not:

      diferencia_2([],_,[]).

      diferencia_2([X|L1],L2,L3):-
         pertenece(X,L2),
         diferencia_1(L1,L2,L3).

      diferencia_2([X|L1],L2,[X|L3]):-
         not(pertenece(X,L2)),
         diferencia_1(L1,L2,L3).
     
Ejercicio 4.2. [Bratko-86 p.130] Definir el predicado no_unif(X,Y) que se
   verifique si X e Y son unificables. Por ejemplo
   ?-no_unif(a,b).   ==> Yes
   ?-no_unif(a,a).   ==> No
   Hacer la versin con corte y la versin con not.

   * Versin con corte:

      no_unif_1(X,Y):-
         X = Y,
         !,
         fail.

      no_unif_1(_,_).

   * Versin con not:

      no_unif_2(X,Y):-
         Not(X=Y).


Ejercicio 4.3. [Bratko-86 p.138] Define el predicado unificable(Lista,Term,
   Lista2) donde Lista2 es la lista de miembros de Lista1 que unifican con
   Term, pero que no estn instanciados por esa unificacin. Por ejemplo:

   ?-unificable([X,b,t(Y)],t(a),L).
   L=[X,t(Y)]

   Fjate que X e Y permanecen sin instanciar en la salida. (Idea: usa
   \+Term1 = Term2. Si Term1 y Term2 unifican, entonces \+Term1 = Term2
   falla y deja Term1 y Term2 sin instanciar.

   *NOTA* : Lo siguente no sale demasiado bien, ya que instancia...

   unificable([],X,[]).
   
   unificable([Term1|L1],Term2,L3):-
      \+ Term1 = Term2,
      unificable(L1,Term2,L3),
      !.
   
   unificable([Term1|L1],Term2,[Term1|L3]):-
      unificable(L1,Term2,L3).

Ejercicio 4.4. [Flach-94 p.52] Considera el siguiente programa:

   p(a,X) :- q(X).
   p(X,Y) :- r(Y,X).
   r(b,a).
   r(c,a).
   q(b).

   1.Dibuja el rbol de resolucin SLD para la pregunta ?- p(M,N).

                            ?- p(M,N)
                    [1]        /    \     [2]
            {M/a, N/X}        /      \       {M/X, N/Y}
                          q(N)         r(N,M)
               {N/b}  [5]  |         [3] / \ [4]
                              {M/a,N/b}/   \{M/a,N/c}
                                            

   2. Observa que la respuesta {M/a, N/b} se obtiene dos veces. Podemos
      poner un corte de manera que esa respuesta slo se obtenga una vez
      manteniendo la tercera respuesta?

      No. Habra que, adems, cambiar el orden:

      p(a,X) :- q(X).
      p(X,Y) :- r(Y,X),!.
      r(c,a).
      r(b,a).
      q(b).

Ejercicio 4.5. [Van Le-93, p.125] Escribir como programa lgico las siguientes
   afirmaciones:

   Si X no est fuera entonces est en casa
   Susana est fuera
   Juan es el marido de Susana

   casa(X):-not(fuera(X)).
   fuera(susana).
   marido(juan,susana).

   Usa el programa para responder a las siguientes preguntas:

   (a) Est Juan en casa?

   ?- casa(juan).
   Yes.

   Esto es porque fuera(juan) = No, y not(No)=Yes.

   (b) Hay alguien en casa?

   ?- casa(_).
   No.

   Porque no hay nadie segn el programa que est en casa (~explcitamente~).

Ejercicio 4.7. [Bratko-86 p.135] Define el predicado separa(Numeros, Pos_C,
   Neg) que separa la lista de nmeros Numeros en dos listas: Una de positivos
   (y cero si lo hubiera), Pos_C y otra de Negativos Neg. Propn dos solucio-
   nes, una sin corte y otra con corte.

   % Sin corte:

   separa1([],[],[]).
   separa1([X|Num],[X|Pos_C],Neg):-
      X >= 0,
      separa1(Num, Pos_C, Neg).

   separa1([X|Num],Pos_C,[X|Neg]):-
      X < 0,
      separa1(Num, Pos_C, Neg).

   % Con corte:

   separa2([],[],[]).
   separa2([X|Num],[X|Pos_C],Neg):-
      X >= 0,
      separa2(Num, Pos_C, Neg),
      !.

   separa2([X|Num],Pos_C,[X|Neg]):-
      separa2(Num, Pos_C, Neg).

Ejercicio 4.8. [Clocksin-94 p. 84] Usar conc para responder a la siguiente
   cuestin: Encontrar todas las listas L tal que al concatenar L con [c,d]
   nos devuelva [a, b, c, d].

   conc([],L2,L2).
   conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3).

   ?-conc(L,[c,d],[a,b,c,d]).

   1. Si obtener prefijos de esta forma fuera el nico uso de conc en nuestro
      programa podramos modificar su definicin para mejorar la eficiencia?

      conc([],L2,L2):-!.
      conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3).

      As solo nos devuelve la primera solucin que encuentra. Si nos ceimos
      nicamente a lo que se pide sabemos que esta solucin es nica, por lo
      que el cambio sera una mejora en eficiencia (la prdida de semntica
      no importa en este caso).

   2. Servira esta modificacin para el uso general de conc?
      No, por lo que se ha dicho antes.

Ejercicio 4.9. [Ex. Dic. CI-98] Definir el predicado suma_pares(L,N) que tome
   como dato de entrada la lista de nmeros enteros L y devuelva la suma N de
   todos los nmeros pares que aparezcan en L. Por ejemplo:

   ?- suma_pares([2,3,4],N).
   N = 6;
   No.

   par(0).
   par(X):-
      X>1,
      N1 is N-2,
      par(N1).

   suma_pares([],0).
   suma_pares([X|L],N):-
      par(X),
      !,
      suma_pares(L,N1),
      N is X+N1.
   suma_pares([X|L],N):-suma_pares(L,N).

Ejercicio 4.10. Definir el predicado exponente_de_dos(N,Exp) que tome como
   entrada un nmero entero positivo y devuelva el valor Exp, que corresponde
   al exponente de 2 en la descomposicin de N como producto de factores
   primos. Por ejemplo

   ?- exponente_de_dos(24,Exp).
      Exp=3;
      No
   ?- exponente_de_dos(49,Exp).
      Exp=0;
      No

   par(0).
   par(N):-
      N>1,
      N1 is N-2,
      par(N1).

   exponente_de_dos(X,N):-
      par(X),
      !,
      X1 is X / 2,
      exponente_de_dos(X1,N1),
      N is N1+1.

   exponente_de_dos(X,0).

< TEMA 5 >----< OTROS PREDICADOS PREDEFINIDOS >---------------------------->8

+ Clasificacin de trminos:

   * Trminos
      + Trminos simples
         - Constantes
            . tomos
            . Nmeros
         - Variables
      + Trminos estructurados

+ Nmeros

   + Nmero: number
      ?- number(5.468).
      Yes
      ?- number(-4).
      Yes
      ?- number(a).
      No

    + Enteros: integer
      ?- integer(4).
      Yes
      ?- integer(2.345).
      No

    + Punto flotante: float
      ?- float(3.072).
      Yes
      ?- float(-7).
      No

    + Redondeo: round
      ?- help(round)
      round(+Expr)
         Evaluates Expr and rounds the result to the nearest integer.
      Yes
      ?- X is round(5.3).
      X = 5
      Yes
      ?- X is round(5.8).
      X = 6
      Yes
      ?- X is round(127/10).
      X = 13
      Yes
      ?- X is round((127/10)*1.34).
      X = 17
      Yes
      ?- X is round(5.5).
      X = 6
      Yes
      ?- X is round(5.4999).
      X = 5
      Yes

+ Suma Segura

   + Definir suma_segura(X,Y,Z) que verifique si X e Y son enteros y Z es la
     suma de X e Y.

   + Programa suma_segura.pl

      suma_segura(X,Y,Z):-
         integer(X),
         integer(Y),
         Z is X+Y.

   + Sesin:

      ?- suma_segura(7,a,Z).
      No

+ tomos y variables: atom, atomic, var y nonvar

   ?- help(atom).
   atom(+term)
      Succeeds if Term is bound to an atom.
   Yes
   ?- help(atomic).
   atomic(+term)
      Succeeds if Term is bound to an atom, string, integer or floating
      point number.

   ?- atom(ana).
   Yes
   ?- atom('Ana').
   Yes
   ?- atom(Ana).           % es una variable
   No
   ?- atom(5.789).         % es un nmero
   No
   ?- atomic(5.789).
   Yes
   ?- var(X).
   X = _G105
   Yes
   ?- X = a, var(X).       % como X est instanciado vale 'a' y no es var.
   No
   ?- nonvar(a).
   Yes

+ Cuenta ocurrencias

   + Definir el predicado cuenta(A,L,N) que se verifique si N es el nmero de
     ocurrencias del tomo A en la lista L.

      cuenta(A,[],0).
      cuenta(A,[B|L],N):-
         atom(B),
         A = B,
         !,
         cuenta(A,B,N1),
         N is N1+1.
      cuenta(A,[B|L],N):-  % solo evala esto si Not(atom(B), A = B).
         cuenta(A,B,N).

   + Si se cambia la definicin anterior suprimiendo el literal atom(B):

      cuenta_1(A,[],0).
      cuenta_1(A,[B|L],N):-
         A = B,
         !,
         cuenta_1(A,B,N1),
         N is N1+1.
      cuenta_1(A,[B|L],N):-  % solo evala esto si Not(A = B).
         cuenta_1(A,B,N).

      Pueden pasar cosas no deseadas como la siguiente:

      ?- cuenta_1(a, [a, b, X, Y], N).
      X = a
      Y = a
      N = 3;
      No

      Vemos como instancia X e Y con 'a' y cuenta ms ocurrencias de las que
      realmente hay del tomo 'a'.
      
+ Los predicados =.., functor, args y name

   * Predicado =..

      Tiene la forma Term =.. List

      Dada una lista List construye un trmino usando como nombre de
      funcin el primer elemento de List y como parmetros los dems ???

      ?- Term =..[padre,juan,luis].
      Term = padre(juan, luis);
      No

   * Predicado functor/3

      Tiene la forma functor(Term, SF, Aridad). donde:
      - Term es un trmino que representa una expresin de funcin.
      - SF representa el nombre de esa funcin.
      - Aridad representa la aridad de esa funcin.

      ?- functor(f(a,b,c),SF,Aridad).
      SF = f
      Aridad = 3;
      No

   * Predicado arg/3

      Tiene la forma arg(N,Term,Argumento).
      - N representa a un nmero de argumento.
      - Term es un trmino.
      - Argumento es el argumento N-simo de Term.

      ?- arg(1,padre(juan,luis),Argumento).
      Argumento = juan;
      No.

   * Predicado name/3

      Tiene la forma name(Term,lista_ASCII).
      - Term representa un trmino.
      - lista_ASCII es una lista formada por los cdigos ASCII de cada uno
        de los caracteres que forman la palabra en Term

      ?- name(dinamic,L).
      L = [100, 105, 110, 97, 109, 105, 99]
      Yes

+ Figuras proporcionales:

   + Las figuras geomtricas se representan como trminos en los que el functor
     indica el tipo de figura y sus argumentos su tamao. Por ejemplo:

     cuadrado(3)
     triangulo(3,4,5)
     circulo(2)

   + Definir el predicado alarga(Figura1,Factor,Figura2) donde Figura1 y
     Figura2 son figuras geomtricas del mismo tipo y el tamao de la Figura2
     es el de la Figura1 multiplicado por Factor.

   + Ejemplo:

      ?- alarga(triangulo(3,4,5),2,F).
      F = triangulo(6,8,10)
      Yes

    + Programa:

      alarga(Figura1, Factor, Figura2):-
         Figura1 =.. [Tipo|Argumentos1],
         multiplica(Argumentos1, Factor, Argumentos2),
         Figura2 =.. [Tipo|Argumentos2].

      multiplica([],_,[]).
      multiplica([X|L1],F,[Y|L2]):-
         Y is X*F,
         multiplica(L1,F,L2).

+ Sustitucin de un trmino:

   + Definir el predicado sustituye(Sub1,Term1,Sub2,Term2) que se verifique
     si Term2 es el trmino obtenido sustituyendo todas las ocurrencias de
     Sub1 en Term1 por Sub2.

    + Ejemplo:

      ?- sustituye(sen(x),2*sen(x)*f(sen(x)),y,T).
      T = 2 * y * f(y)
      Yes

      ?- sustituye(a+b,f(a,A+B),c,T).
      A = a
      B = b
      T = f(a, c)
      Yes

    + Programa "sustituye.pl":

      sustituye(Sub1,Term1,Sub2,Sub2):-
         Sub1 = Term1, !.
      sustituye(Sub1,Term1,Sub2,Term1):-
         atomic(Term1), !.
      sustituye(Sub1,Term1,Sub2,Term2):-
         Term1=..[Functor|Args1],
         sustituye_lista(Sub1,Args1,Sub2,Args2),
         Term2=..[Functor|Args1].

      sustituye_lista(_,[],_,[]).
      sustituye_lista(Sub1,[T1|T1s],Sub2,[T2,|T2s]):-
         sustituye(Sub1,T1,Sub2,T2),
         sustituye_lista(Sub1,T1s,Sub2,T2s).

+ Trminos cerrados:

   + Definir el predicado cerrado(T) que se verifique sii T es un trmino
     cerrado, es decir, sin variables.

   + Ejemplo:

      ?- cerrado(f(a+b)).
         Yes
      ?- cerrado(f(a+X)).
         No

   + Programa "cerrado.pl":

      cerrado(T):-
         nonvar(T),
         T = ..[_|Args],
         cerrados(Args).

      cerrados([]).

      cerrados([X|L]):-
         cerrado(X),
         cerrados(L).

   + El predicado predefinido "ground" tiene el mismo efecto que "cerrado".

+ El predicado maplist:

   + Definir el predicado n_maplist(+Predicado,?Lista1,?Lista2) que aplique
     el predicado sobre los sucesivos pares de elementos de la Lista1 y la
     Lista2 y falle si el predicado no puede aplicarse a un par.

   + Ejemplos:

      ?- n_maplist(succ,[2,4],[3,5]).
      Yes
      ?- n_maplist(succ,[2,4],Y).
      Y = [3, 5]
      Yes
      ?- n_maplist(succ,X,[3,5]).
      X = [2, 4]
      Yes
      ?- n_maplist(succ,[0,4],[3,5]).
      No

   + Programa "n_maplist.pl":

      n_maplist(_,[],[]).

      n_maplist(Pred,[X|L1],[Y|L2]):-
         P1=..[Pred,X,Y],
         P1,
         n_maplist(Pred,L1,L2).

   + El predicado predefinido maplist tiene el mismo efecto que n_maplist.

+ Nmeros de Turing:

   + Un nmero entero es de Turing si es igual al cuadrado de la suma de sus
     cifras. Definir la relacin numero_de_turing(N) que se verifique si N es
     un nmero de Turing y calcular los nmeros de Turing menores que 1.000.

   + Programa "turing.pl":

      numero_de_turing(N):-
         cifras(N,L),
         suma_cifras(L,M),
         N =:= M**2.

      cifras(N,L):-
         name(N,L1),
         reduce(L1,L).

      reduce([X|R],[Y|S]):-
         Y is X -48,
         reduce(R,S).
      reduce([],[]).

      suma_cifras([X|R],N):-
         suma_cifras(R,M),
         N is M + X.
      suma_cifras([],0).

   + Sesin

      ?- between(0,1000,N), numero_de_turing(N).
      N = 0;
      N = 1;
      N = 81;
      No

+ Unificacin =, \=
      
   ?- f(a,X)=f(Y,b).
   X = b
   Y = a;
   No
   ?- [X,YT,Z]=[Y,Z,X].
   X = _G159
   Y = _G159
   Z = _G159;
   No
   ?- f(a,b)\=f(_X,_X).
   Yes

+ Test de ocurrencia

   + Programa "monstruo.pl":
      monstruo:- X = f(X).

   + Sesin:

      ?- monstruo.
      Yes

+ Aritmtica is, =:=, =\=

   ?- X is 3+2.
   X = 5;
   No

   ?- 5 is 3+X.
   [WARNING: Arguments are not sufficiently instantiated]
    ^ Exception: (  7) 5 is 3+_G118 ? creep
   [WARNING: Unhandled exception]

   ?- 11 - 3 =:= 2^3.
   Yes

   ?- 11 - 3 =:= 2^X.
   [WARNING: Arguments are not sufficiently instantiated]
    ^ Exception: (  7) 11-3=:=2^_G157 ? creep
   [WARNING: Unhandled exception]

   ?- 11 - 3 =\= 2^7.
   Yes

   ?- 11 - X =\= 2^7.
   [WARNING: Arguments are not sufficiently instantiated]
    ^ Exception: (  7) 11-_G154=\=2^7 ? creep
   [WARNING: Unhandled exception]

+ Identidad ==, \==

   ?- f(a,b)==f(a,b).
   Yes
   ?- f(a,b)==f(a,X).
   No
   ?- f(a,Y)==f(a,X).
   No
   ?- X \== Y.
   X = _G111
   Y = _G112
   Yes
   ?- _X \== _Y.
   Yes
   ?- f(A,g(B,i),Z)==f(A,g(B,i),Z).
   A = _G240
   B = _G237
   Z = _G242
   Yes

+ Cuenta ocurrencias (II):

   + Definir el predicado cuenta(A,L,N) que se verifique si N es el nmero de
     ocurrencias idnticas al tomo A en la lista L.

   + Ejemplos:

      ?- cuenta(a,[a,b,a,a],N).
         N = 3
         Yes
      ?- cuenta(a,[a,b,X,Y],N).
         X = _G313
         Y = _G316
         N = 1
         Yes

   + Programa "cuenta2.pl":

      cuenta(_[],0).
      cuenta(A,[B|L],N):-
         A == B,
         cuenta(A,L,M),
         N is M + 1.
      cuenta(A,[B|L],N):-
         A \== B,
         cuenta(A,L,N).

< TEMA 6 >----------------------------------------------------------------->8

+ Predicados assert y retract:

   + assert(+Term) inserta un hyecho o una clusula en la base de conocimien-
     tos. Term es insertado como litma clusula del predicado correspondien-
     te.

   + retract(+Term) elimina la primera clusula de la base de conocimientos
     que unifica con Term.

   + Ejemplos:

      ?- hace_frio.
      [WARNING: Undefined predicate: `hace_frio/0]
      No
      ?- assert(hace_frio).
      Yes
      ?- hace_frio.
      Yes
      ?- retract(hace_frio).
      Yes
      ?- hace_frio.
      No

+ El predicado listing

   + listing(+Pred) lista las clusulas en cuya cabeza aparece el predicado
     Pred.

   + Ejemplos:

     ?- listing(select).
     select([A|B], A, B).
     select([A|B], C, [A|D]) :- select(B, C, D).
     Yes
     ?- assert( (gana(X,Y) :- rapido(X), lento(Y))).
     X = _G445
     Y = _G446
     Yes
     ?- listing(gana).
     gana(A, B) :-
         rapido(A),
         lento(B).
     Yes
     ?- assert(rapido(juan)),assert(lento(jose)),assert(lento(luis)).
     Yes
     ?- gana(X,Y).
     X = juan Y = jose ;
     X = juan Y = luis ;
     No
     ?- retract(lento(X)).
     X = jose ;
     X = luis ;
     ?- gana(X,Y).
     No

+ Los predicados asserta y assertz:

   + asserta(+Term) equivale a assert/1, pero Term es insertado como primera
     clusula del predicado correspondiente.

   + assertz(+Term) equivale a assert/1.

   + Ejemplos:

     ?- assert(p(a)),assertz(p(b)), asserta(p(c)).
     Yes
     ?- p(X).
     X = c ;
     X = a ;
     X = b ;
     No
     ?- listing(p).
     p(c).
     p(a).
     p(b).
     Yes

+ Los predicado retractall y abolish:

   + retractall(+Head) elimina de la base de conocimientos todas las clusulas
     cuya cabeza unifica con Head.

   + abolish(+SimbPred/+Arid) elimina de la base de conocimientos todas las
     clusulas que en su cabeza aparece el smbolo de predicado SimbPred/Arid.

   + abolish(+SimbPred, +Aridad) es equivalente a abolish(+SimbPred/+Aridad).

   + Ejemplo:

     ?- assert(p(a)), assert(p(b)).
     Yes
     ?- retractall(p(_)).
     Yes
     ?- p(a).                       % Elimina las clusulas, pero el nombre
     No                             % del predicado sigue estando en la BC.
     ?- assert(p(a)), assert(p(b)).
     Yes
     ?- abolish(p/1).               % Elimina de la BC incluso el nombre del
     Yes                            % predicado.
     ?- p(a).
     [WARNING: Undefined predicate: `p/1]
     No
     ?- assert(f(a,b)).
     Yes
     ?- f(X,Y).
     X = a Y = b ;
     No
     ?- asserta(f(a,a)),assertz(f(b,b)).
     Yes
     ?- f(X,Y).
     X = a Y = a ;
     X = a Y = b ;
     X = b Y = b ;
     No
     ?- listing(f).
     f(a,a).
     f(a,b).
     f(b,b).
     ?- retract(f(_,a)).
     Yes
     ?- listing(f).
     f(a,b).
     f(b,b).
     ?- assert(f(b,a)).
     Yes
     ?- listing(f).
     f(a,b).
     f(b,b).
     f(b,a).
     ?- retractall(f(b,_)).
     Yes
     ?- listing(f).
     f(a,b).
     ?- abolish(f/2).
     Yes
     ?- listing(f).
     [WARNING: No predicates for `f]
     No

+ Multiplicaciones (tabla.pl):

   + crea_tabla aade los hechos producto(X,Y,Z) donde X e Y son nmeros de 0
     a 9 y Z es el producto de X e Y.

     crea_tabla:-
         L = [0,1,2,3,4,5,6,7,8,9],
         member(X,L),
         member(Y,L),
         Z is X*Y,
         assert(producto(X,Y,Z)),
         fail.

     crea_tabla.

   + Sesin:

     ?- crea_tabla.
     Yes
     ?- listing(producto).
     producto(0,0,0).
     producto(0,1,0).
     (...)
     producto(9,8,72).
     producto(9,9,81).
     Yes

   + Determinar las descomposiciones de 6 en producto de 2 nmeros.

     ?- producto(X,Y,6).
     A = 1 B = 6;
     A = 2 B = 3;
     A = 3 B = 2;
     A = 6 B = 1;
     No

+ Programa

   :- dynamic r/2

   relacionados(X,Y):-
      assert(r(X,Y)).

   no_relacionados(X,Y):-
      retract(r(X,Y)).

+ Sesin

   ?- r(X,Y).
   No
   ?- relacionados(a,b).
   Yes
   ?- r(X,Y).
   X = a Y = b;
   No
   ?- no_relacionados(a,b).
   Yes
   ?- r(a,b).
   No
   ?- relacionados(a,b),relacionados(c,d).
   Yes
   ?- r(X,Y).
   X = a Y = b;
   X = c Y = d;
   No

+ El predicado findall:

   % Devuelve el resultado en una lista.

   ?- assert(clase(a,voc)),
      assert(clase(b,con)),
      assert(clase(e,voc)),
      assert(clase(c,con)).
   Yes

   ?- findall(X,clase(X,voc),L).
   X = _G331
   L = [a, e]
   Yes

   ?- findall(_X,clase(_X,voc),L).
   L = [a, e]
   Yes

   ?- findall(_X,clase(_X,_Clase),L).
   L = [a, b, e, c]
   Yes

   ?- findall(X,clase(X,vocal),L).
   L = []
   Yes

   ?- findall(X,(member(X,[c,b,c]),member(X,[c,b,a])),L).
   X = _G373
   L = [c, b, c]
   Yes

   ?- findall(X,(member(X,[c,b,c]),member(X,[1,2,3])),L).
   X = _G373
   L = []
   Yes

+ El predicado bagof:

   % Devuelve el resultado en un multiconjunto y no ordena la respuesta.

   ?- bagof(X,clase(X,voc),L).
   X = _G331
   L = [a, e]
   Yes

   ?- bagof(X,clase(X,Clase),L).
   X = _G343 Clase = voc L = [a, e] ;
   X = _G343 Clase = con L = [b, c] ;
   No

   % L = {X: (existe Y)[clase(X,Y)]}
   ?-bagof(X,Y^clase(X,Y),L).
   X = _G379 Y = _G380  L = [a, b, e, c] ;
   No   

   ?-bagof(_X,_Y^clase(_X,_Y),L).
   L = [a, b, e, c] ;
   No

   ?- bagof(letra(_X),_Y^clase(_X,_Y),L).
   L = [letra(a), letra(b), letra(e), letra(c)]
   Yes

   ?- bagof(X,clase(X,vocal),L).
   No                            % Recordemos que findall devuelve L = []

   ?- bagof(X,(member(X,[c,b,c]),member(X,[c,b,a])),L).
   X = _G361
   L = [c, b, c] ;
   No

   ?- bagof(X,(member(X,[c,b,c]),member(X,[1,2,3])),L).
   No

+ El predicado setof:

   % Devuelve la respuesta en un conjunto, y ordena la respuesta, elminando
   % las repeticiones.

   ?- setof(X,clase(X,voc),L).
   X = _G331
   L = [a, e]
   Yes

   ?- setof(X,clase(X,Clase),L).
   X = _G343 Clase = voc L = [a, e] ;
   X = _G343 Clase = con L = [b, c] ;
   No

   % L = {X: (existe Y)[clase(X,Y)]}
   ?-setof(X,Y^clase(X,Y),L).
   X = _G379 Y = _G380  L = [a, b, c, e] ;
   No   

   ?-setof(_X,_Y^clase(_X,_Y),L).
   L = [a, b, c, e] ;
   No

   ?- setof(letra(_X),_Y^clase(_X,_Y),L).
   L = [letra(a), letra(b), letra(c), letra(e)]
   Yes

   ?- setof(X,clase(X,vocal),L).
   No                            % Recordemos que findall devuelve L = []

   ?- setof(X,(member(X,[c,b,c]),member(X,[c,b,a])),L).
   X = _G361
   L = [b, c] ;
   No

   ?- setof(X,(member(X,[c,b,c]),member(X,[1,2,3])),L).
   No
   
+ Operaciones conjuntistas:

   + interseccion(S,T,U) se verifican si U es la interseccin de S y T. Por
     ejemplo,

         ?- interseccion([1,4,2],[2,3,4],U).
         U = [2, 4]
         Yes

      interseccion(S,T,U):-
         setof(X,(member(X,S),member(X,T)),U).

   + diferencia(S,T,U) se verifica si U es la diferencia de los conjuntos S y
     T. Por ejemplo,

         ?- diferencia([5,1,2],[2,3,4],U).
         U = [1, 5]
         Yes

      diferencia(S,T,U):-
         setof(X,(member(X,S),not(member(X,T))),U).

   + n_union(S,T,U) se verifica si U es la unin de S y T. Por ejemplo,

         ?- n_union([|,2,4],[2,3,4],U).
         U = [1, 2, 3, 4]
         Yes

      n_union(S,T,U):-
         setof(X,(member(X,S);member(X,T)),U).

   + partes(X,L) se verifica si L es el conjunto de las partes de L. Por ejem-
     plo,

         ?- partes([a,b],L).
         L = [[], [a], [a,b], [b]]
         Yes

      partes(X,L):-
         setof(Y,subconjunto(Y,X),L).

      subconjunto([],[]).

      subconjunto([X|L1],[X|L2]):-
         subconjunto(L1,L2).

      subconjunto(L1,[_|L2]):-
         subconjunto(L1,L2).

+ Definicin de findall:

   n_findall(X,Objetivo,_Lista_de_X):-
      Objetivo,
      assert(almacena(X)),
      fail.

   n_findall(_X,_Objetivo,Lista_de_X):-
      assert(almacena(fin)),
      recoge(Lista_de_X).

   recoge(L):-
      retract(almacena(X)),
      !,
      recoge_aux(X,L).

   recoge_aux(fin,[]):-!.

   recoge_aux(X,[X|L]):-
      recoge(L).

   + Sesin:

     ?- assert(p(a)), assert(p(b)).
     Yes
     ?- listing(p).
     p(a).
     p(b).
     Yes
     ?- n_findall(X,p(X),L).
     X = _G163
     L = [a, b]
     Yes

+ El predicado apply:

   + n_apply(+Term,+Lista) se verifica si es demostrable Term despus de au-
     mentar el nmero de sus argumentos con los elementos de Lista. Tenemos
     el predicado predefinido apply.

   + Ejemplo:

      ?- plus(1,2,X).
      X = 3
      Yes
      ?- n_apply(plus,[1,2,X]).
      X = 3 ;
      No
      ?- n_apply(plus(1),[2,X]).
      X = 3
      Yes
      ?- n_apply(plus(1,2),[X]).
      X = 3
      Yes
      ?- n_apply(append([1,2]),[X,[1,2,3,4,5]]).
      X = [3, 4, 5] ;
      No

   + Programa "n_apply.pl"

      n_apply(Term,List):-
         Term = .. [Pred|Args1],
         append(Args1,List,Args2),
         Atomo = .. [Pred|Args2],
         Atomo.

+ Patrones aplicativos y maplist:

   + padre(X,P) se verifica si P es el padre de X

      padre(beatriz,andres).
      padre(david,carlos).
      padre(elisa,ernesto).

   + madre(X,M) se verifica si M es la madre de X

      madre(beatriz,maria).
      madre(david,eva).
      madre(elisa,carmen).

   + padres(L1,L2) se verifica si cada elemento de L2 es el padre del corres-
     pondiente elemento de L1. Por ejemplo,

         ?- padres([beatriz,david,elisa],L).
         L = [andres, carlos, ernesto]
         Yes

      padres([],[]).
      padres([X|L1],[Y|L2]):-
         padre(X,Y),
         padres(L1,L2).

   + madres(L1,L2) se verifica si cada elemento de L2 es la madre del corres-
     pondiente elemento de L1. Por ejemplo,

         ?- madres([beatriz,david,elisa],L).
         L = [maria, eva, carmen]
         Yes

      madres([],[]).
      madres([X|L1],[Y|L2]):-
         madre(X,Y),
         madres(L1,L2).

   + Preguntas con maplist:

      ?- maplist(padre,[beatriz,david,elisa],L).
      L = [andres, carlos, ernesto]
      Yes

      ?- maplist(madre,[beatriz,david,elisa],L):
      L = [maria, eva, carmen]
      Yes

   + Definicin de maplist:

      n_maplist(_,[],[]).

      n_maplist(R,[X1|L1],[X2|L2]):-
         apply(R,[X1,X2]),
         n_maplist(R,L1,L2).
