miércoles, 4 de septiembre de 2013

Tema 1- Examples/Ejercicios

1. Tres enteros positivos x, y, z constituyen una terna pitagórica si x2+y2=z2, es decir, si son los lados
de un triángulo rectángulo.
a) Define la función
          esTerna :: Integer -> Integer -> Integer -> Bool
que compruebe si tres valores forman una terna pitagórica. Por ejemplo:
          Main> esTerna 3 4 5 Main> esTerna 3 4 6
          True False
b) Es fácil demostrar que para cualesquiera x e y enteros positivos con x>y, la terna (x2-y2, 2xy,    x2+y2) es pitagórica. Usando esto, escribe una función terna que tome dos parámetros y devuelva una
terna pitagórica. Por ejemplo:
          Main> terna 3 1
         (8,6,10)
         Main> esTerna 8 6 10
        True
c) Lee y entiende la siguiente propiedad, para comprobar que todas las ternas generadas por la
función terna son pitagóricas:
p_ternas x y = x>0 && y>0 && x>y ==> esTerna l1 l2 h where (l1,l2,h) = terna x y
d) Comprueba esta propiedad usando QuickCheck (recuerda importar Test.QuickCheck al principio
de tu programa y copiar la propiedad en tu fichero). Verás que la respuesta es parecida a:
         Main> quickCheck p_ternas
         *** Gave up!Passed only 62 tests
lo que indica que, aunque sólo se generaron 62 casos de pruebas con las condiciones precisas,
todos estos los casos pasaron la prueba.

esTerna :: Integer -> Integer -> Integer -> Bool
esTerna x y z = x^2+y^2==z^2

terna :: Integer -> Integer -> (Integer,Integer,Integer)
terna x y = if x>y then ( x^2 -  y^2 , 2*x*y ,  x^2 +  y^2) else error "no hay terna"

--Otra forma
terna x y =( x^2-y^2, 2*x*y, x^2+y^2)   --el uso del if then else equivale al uso de guardas 
                 |x>y
                 |otherwise error "no hay terna"

p_ternas x y = x>0 && y>0 && x>y ==> esTerna l1 l2 h
  where
    (l1,l2,h) = terna x y
-- *Main> quickCheck p_ternas
-- *** Gave up! Passed only 77 tests.


2. Define una función polimórfica intercambia :: (a,b) -> (b,a) que intercambie de posición los datos de la tupla:
           Main> intercambia (1,True) Main> intercambia ('X','Y')
           (True,1) ('Y','X')


intercambia :: (a,b)->(b,a)
intercambia (x,y) = (y,x)


3. Este ejercicio versa sobre ordenación de tuplas.

a) Define una función sobrecargada para tipos con orden ordena2 :: Ord a => (a,a) -> (a,a)
que tome una tupla con dos valores del mismo tipo y la devuelva ordenada de menor a mayor:
                Main> ordena2 (10,3) Main> ordena2 ('a','z')(3,10) ('a','z')
Copia en tu fichero las siguientes propiedades relativas a la función ordena2:
             p1_ordena2 x y = enOrden (ordena2 (x,y))
                     where enOrden (x,y) = x<=y
                            p2_ordena2 x y = mismosElementos (x,y) (ordena2 (x,y))
                               where
                                  mismosElementos (x,y) (a,b) = (x==a && y==b) || (x==b && y==a)
               (o, alternativamente, (x,y)==(a,b)||(x,y)==(b,a))
Entiende lo que cada una significa, y compruébalas usando QuickCheck.
b) Define una función sobrecargada para tipos con orden
            ordena3 :: Ord a => (a,a,a) -> (a,a,a)
que tome una tupla con tres valores del mismo tipo y la devuelva ordenada, con los elementos de
menor a mayor:
             Main> ordena3 (10,3,7) (3,7,10)
c) Escribe propiedades análogas a las del apartado anterior pero para esta función, y compruébalas
usando QuickCheck.

ordena2 :: Ord a => (a,a) -> (a,a)
ordena2 (x,y) 

              | x<=y = (x,y)
              | otherwise = (y,x)

p1_ordena2 x y = enOrden (ordena2 (x,y))
  where enOrden (x,y) = x<=y

p2_ordena2 x y = mismosElementos (x,y) (ordena2 (x,y))
  where
    mismosElementos (x,y) (a,b) = (x==a && y==b) || (x==b && y==a)

--otra forma
    mismosElementos (x,y) (a,b) = (x,y)==(a,b) || (x,y)==(b,a)

ordena3 :: Ord a => (a,a,a) -> (a,a,a)
ordena3 (x,y,z) |x>y = ordena3(y,x,z)
                |y>z = ordena3(x,z,y)
                |x>z = ordena3(z,x,y)
                |otherwise = (x,y,z)


p1_ordena3 x y z = enOrden(ordena3(x,y,z))
                where enOrden(x,y,z) = x<=y && y<=z


4. Aunque ya existe una función predefinida (max :: Ord a => a -> a -> a) para calcular el
máximo de dos valores, el objetivo de este ejercicio es que definas tu propia versión de dicha
función.

a) Como no está permitido redefinir una función predefinida, define una nueva y llámala
           max2 ::Ord a => a -> a -> a
de forma que satisfaga:
          Main> 10 `max2` 7 Main> max2 'a' 'z' 10 'z'
b) Define las siguientes propiedades que debería verificar tu función max2 y compruébalas con
QuickCheck (recuerda importar Test.QuickCheck al principio de tu programa):
        i. p1_max2: el máximo de dos números x e y coincide o bien con x o bien con y.
        ii. p2_max2: el máximo de x e y es mayor o igual que x , así como mayor o igual que y.
        iii. p3_max2: si x es mayor o igual que y, entonces el máximo de x e y es x.
        iv. p4_max2: si y es mayor o igual que x, entonces el máximo de x e y es y.

max2:: Ord a => a -> a -> a
max2 x y
  | x>y = x
  | otherwise = y

p1_max2 x y = True ==> max2 x y == x || max2 x y == y
p2_max2 x y = True ==> max2 x y >= x || max2 x y >= y
p3_max2 x y = x>=y ==> max2 x y == x
p4_max2 x y = y>=x ==> max2 x y == y


5. Define una función sobrecargada para tipos con orden 
        entre :: Ord a => a -> (a,a) -> a 
que tome un valor x además de una tupla con dos valores (max,min) y compruebe si x pertenece al
intervalo determinado por min y max, es decir, si x ∈ [min,max], devolviendo True o False según
corresponda. Por ejemplo:
           Main> 5 `entre` (1,10) Main> entre 'z' ('a','d')
           True False


entre :: Ord a => a -> (a, a) -> Bool
entre x (p,q)  | x>p && x<q = True
               | otherwise  =  False

--Otra forma
entre x(p,q)=x>=y && x<=q

 6. Define una función sobrecargada para tipos con igualdad
iguales3 :: Eq a => (a,a,a) -> Bool
que tome una tupla con tres valores del mismo tipo y devuelva True si todos son iguales. Por
ejemplo:
        Main> iguales3 ('z','a','z')
        False
        Main> iguales3 (5+1,6,2*3)
       True


iguales3 :: Eq a => (a,a,a)-> Bool
iguales3 (x,y,z)
  | x==y && y==z   = True    
  | otherwise = False

--Otra forma
ifguales3 (x,y,z) = x==y && y==z

7. Recuerda que el cociente y el resto de la división de enteros se corresponde con las funciones
predefinidas div y mod.
a) Define una función descomponer que, dada una cantidad positiva de segundos, devuelva la
descomposición en horas, minutos y segundos en forma de tupla, de modo que los minutos y
segundos de la tupla estén en el rango 0 a 59. Por ejemplo:
descomponer 5000  (1,23,20) descomponer 100  (0,1,40)
Para ello, completa la siguiente definición:
      type TotalSegundos = Integer
      type Horas = Integer
      type Minutos = Integer
      type Segundos = Integer
      descomponer :: TotalSegundos -> (Horas,Minutos,Segundos)
      descomponer x = (horas, minutos, segundos)
      where
      horas = ...
      ...
b) Comprueba la corrección de tu función verificando con QuickCheck que cumple la siguiente
propiedad:
      p_descomponer x = x>=0 ==> h*3600 + m*60 + s == x
      && entre m (0,59)
      && entre s (0,59)
      where (h,m,s) = descomponer x


-- Para este ejercicio nos interesa utilizar la función predefinida en Prelude:
--              divMod :: a -> a -> (a, a)
-- que calcula simultáneamente el cociente y el resto:
--
--   *Main> divMod 30 7
--   (4,2)

type TotalSegundos = Integer
type Horas         = Integer
type Minutos       = Integer
type Segundos      = Integer

descomponer :: TotalSegundos -> (Horas,Minutos,Segundos)
descomponer x = (horas, minutos, segundos)
   where
     (horas,resto)      = divMod x 3600
     (minutos,segundos)    = divMod resto 60
    
    
p_descomponer x = x>=0 ==> h*3600 + m*60 + s == x
                           && m `entre` (0,59)
                           && s `entre` (0,59)
     where (h,m,s) = descomponer x
-- *Main> quickCheck p_descomponer
-- +++ OK, passed 100 tests.


8. Sea la siguiente definición que representa que un euro son 166.386 pesetas:
      unEuro :: Double
      unEuro = 166.386
a) Define una función pesetasAEuros que convierta una cantidad (de tipo Double) de pesetas en los
correspondientes euros. Por ejemplo:
pesetasAEuros 1663.86  10.0
b) Define la función eurosAPesetas que convierta euros en pesetas. Por ejemplo:
eurosAPesetas 10  1663.86
c) Sea la siguiente propiedad, que establece que si pasamos una cantidad de pesetas a euros y los
euros los volvemos a pasar a pesetas, obtenemos las pesetas originales (es decir, que las funciones
son inversas):
       p_inversas x = eurosAPesetas (pesetasAEuros x) == x
Compruébala con QuickCheck para ver que no se verifica. ¿por qué falla? (pista: estamos trabajando
con números flotantes).

unEuro :: Double
unEuro = 166.386

pesetasAEuros ::  Double -> Double
pesetasAEuros x = x/unEuro

eurosAPesetas ::  Double -> Double
eurosAPesetas x = x*unEuro

p_inversas :: Double -> Bool
p_inversas x = eurosAPesetas (pesetasAEuros x) ~= x
-- p_inversas x no se cumple debido a que las pruebas las ejecuta con numeros en punto flotante y pueden variar unas milesimas y no encajar en la igualdad



9. Sea el siguiente operador que comprueba si dos valores de tipo Double son aproximadamente
iguales:
        infix 4 ~=(~=) :: Double -> Double -> Bool
        x ~= y = abs (x-y) < epsilon
            where epsilon = 1/1000
Por ejemplo: (1/3) ~= 0.33  False (1/3) ~= 0.333  True
Copia esta definición de operador en tu fichero de programa, y cambia la propiedad p_inversas
del ejercicio anterior para que verifique que si pasamos una cantidad de pesetas a euros y los euros
los volvemos a pasar a pesetas, obtenemos las pesetas originales aproximadamente. Comprueba
con QuickCheck que esta propiedad sí se verifica.



infix 4 ~=
(~=) :: Double -> Double -> Bool
x ~= y = abs (x-y) < epsilon
  where epsilon = 1/1000


10. Consideremos la ecuación de segundo grado ax2 + bx + c = 0.
a) Define una función raíces que tome tres parámetros (correspondientes a los coeficientes a, b y c
de la ecuación) y devuelva una tupla con las dos soluciones reales de la ecuación (para calcular la
raíz cuadrada, usa la función predefinida sqrt). Recuerda que el discriminante se define como b24ac
y que la ecuación tiene raíces reales si el discriminante no es negativo. Por ejemplo:
          raíces 1 (-2) 1.0  (1.0,1.0)
          raíces 1.0 2 4  Exception: Raíces no reales
b) Sea la siguiente propiedad que comprueba que las valores devueltos por la función raíces son
efectivamente raíces de la ecuación:
         p1_raíces a b c = esRaíz r1 && esRaíz r2
             where
                (r1,r2) = raíces a b c
       esRaíz r = a*r^2 + b*r + c ~= 0
Comprueba esta propiedad con QuickCheck y verifica que falla. Piensa por qué falla, y añade
condiciones a la propiedad para que no falle, es decir, completa las interrogaciones:
          p2_raíces a b c = ??????? && ?????? ==> esRaíz r1 && esRaíz r2
             where
                 (r1,r2) = raíces a b c
         esRaíz r = a*r^2 + b*r + c ~= 0
de forma que se verifique el siguiente diálogo:
         Main> quickCheck p2_raíces
         +++ OK, passed 100 tests




raices :: Double-> Double -> Double ->(Double,Double)
raices a b c
  | b^2-4*a*c>0 =  ((-b+sqrt (b^2- 4*a*c))/2*a ,(-b-sqrt (b^2- 4*a*c))/2*a)
  | otherwise   =  error"Raices no reales"

p1_raices :: Double-> Double -> Double ->Bool
p1_raices a b c = esRaíz r1 && esRaíz r2
  where
    (r1,r2) = raices a b c
    esRaíz r = a*r^2 + b*r + c ~= 0


--en cuanto una raiz sea negativa esto no funciona, ver
p2_raices :: Double-> Double -> Double ->Property
p2_raices a b c = (a,b,c)/=(0,0,0) && (b*b-4*a*c) >= 0 ==> esRaiz r1 && esRaiz r2
    where
      (r1,r2) = raices a b c
      esRaiz r = a*r^2 + b*r + c ~= 0



11. Define una función esMúltiplo sobrecargada para tipos integrales que tome dos valores x e y, y
devuelva True si x es múltiplo de y. Por ejemplo:
            esMúltiplo 9 3  True
            esMúltiplo 7 3  False

esMultiplo :: (Integral a) => a->a->Bool
esMúltiplo x y = if mod x y ==0 then True else False


12. Define el operador de implicación lógica (==>>) :: Bool -> Bool -> Bool de forma que
sea asociativo a la izquierda, con precedencia menor que los operadores conjunción y disyunción:
         Main> 3 < 1 ==>> 4 > 2
         True
         Main> 3 < 1 || 3 > 1 ==>> 4 > 2 && 4 < 2
         False
Ayuda: puedes escribir ecuaciones directamente para la definición del operador, o bien patrones,
completando definiciones tales como:
        False ==>> y = y
        ??? ==>> ??? = ???

infixl 1 ==>>
(==>>) :: Bool->Bool->Bool
False ==>> y=True
True ==>> y=y                             --Devuelve lo que valga y




13. Los años bisiestos son los años múltiplos de 4. Una excepción a esta regla son los años múltiplos de 100, que sólo se consideran bisiestos si además son múltiplos de 400. Define una función
esBisiesto que tome como parámetro un año y devuelva True si es bisiesto. Por ejemplo:
        esBisiesto 1984 True
       esBisiesto 1985  False
       esBisiesto 1800  False
       esBisiesto 2000  True
Ayuda: utiliza el operador de implicación lógica y la siguiente frase: “n es bisiesto si satisface las dos
condiciones siguientes: (a) es múltiplo de 4, y (b) si n es múltiplo de 100 entonces n es múltiplo de
400”.

esBisiesto :: Integral a => a-> Bool
esBisiesto x = esMultiploB x
  where
    esMultiploB x = esMúltiplo x 4 && esMúltiplo x 100 ==>> esMúltiplo x 400


--Otra forma

esBisiesto :: Integer->Bool
esBisiesto x = esMultiplo x 4 && (esMultiplo x 100 ==>> esMultiplo x 400)

14. Aunque ya existe en Haskell el operador predefinido (^) para calcular potencias, el objetivo de este
problema es que definas tus propias versiones recursiva de este operador.
a) A partir de la propiedad
   


define una función recursiva potencia' que tome un entero b y un exponente natural n y devuelva bn. Por ejemplo:
        potencia 2 3  8
b) A partir de la siguiente propiedad:
define una función recursiva potencia' que tome un entero b y un exponente natural n y devuelva bn. Por ejemplo:
        potencia' 2 3  8 potencia' 2 4  16
c) Comprueba con QuickCheck la corrección de ambas funciones mediante la siguiente propiedad:
       p_pot b n = n>=0 ==> potencia b n == sol && potencia' b n == sol
           where sol = b^n
d) Teniendo en cuenta que elevar al cuadrado equivale a realizar un producto, determina el número
de productos que realizan ambas funciones para elevar cierta base a un exponente n.
Ayuda: para analizar la eficiencia de potencia' considera exponentes que sean potencia de 2.



potencia :: Integer-> Integer -> Integer
otencia x y
  | y== 0        = 1
  | otherwise   = potencia x (y-1) * x


potencia' ::   Integer-> Integer -> Integer
potencia' x y
  | mod y 2 == 0        = (potencia x (div y 2))^2
  | otherwise           = ((potencia x (div (y-1) 2))^2) * x

p_pot :: Integer->Integer->Property
p_pot b n = n>=0 ==> potencia b n == sol && potencia' b n == sol
  where sol = b^n
 

15. Dado un conjunto finito con todos sus elementos diferentes, llamamos permutación a cada una de
las posibles ordenaciones de los elementos de dicho conjunto. Por ejemplo, para el conjunto
{1,2,3}, existen un total de 6 permutaciones de sus elementos: {1,2,3}, {1,3,2}, {2,1,3}, {2,3,1}, {3,1,2}
y {3,2,1}. El número de permutaciones posibles para un conjunto con n elementos viene dada por el
factorial de n (se suele escribir n!), que se define como el producto de todos los números naturales
menores o iguales a n. Escribe una función factorial que tome como parámetro un número
natural y devuelva su factorial. Dado que el factorial crece muy rápido, usa el tipo Integer, es
decir, factorial :: Integer -> Integer. Por ejemplo:
        factorial 3  6 factorial 20  2432902008176640000


factorial :: Integer -> Integer
factorial x
  | x==0        = 1
  | otherwise   = factorial (x-1) * x


16. Este ejercicio estudia la división entera (exacta) de números enteros.
a) Define una función divideA que compruebe si su primer argumento divide exactamente al
segundo. Por ejemplo:
          2 `divideA` 10  True 4 `divideA` 10  False
b) Lee, entiende y comprueba con QuickCheck la siguiente propiedad referente a la función divideA:
          p1_divideA x y = y/=0 && y `divideA` x ==> div x y * y == x
c) Escribe una propiedad p2_divideA para comprobar usando QuickCheck que si un número divide a
otros dos, también divide a la suma de ambos.

divideA :: Integer->Integer -> Bool
divideA x y = mod y x ==0

p1_divideA :: Integer->Integer -> Property
p1_divideA x y = y/=0 && y `divideA` x ==> div x y * y == x

p2_divideA :: Integer->Integer->Integer -> Property
p2_divideA a b c = a/=0 && b/=0 && c/=0 && divideA a b && divideA b c ==> divideA a (b+c)






17. La mediana de un conjunto de valores es aquel valor tal que el 50% de los valores del conjunto son menores o iguales a él, y los restantes mayores o iguales. Queremos definir una función para
calcular la mediana de los valores de una tupla de cinco elementos
        mediana :: Ord a => (a,a,a,a,a) -> a
de forma que se tenga:
        mediana (3,20,1,10,50)  10
Observa que se satisface 1 , 3 ≤ 10 ≤ 20,50. Teniendo en cuenta este detalle, define la función a
través de ecuaciones con guardas, completando el siguiente esquema:
          
mediana :: Ord a => (a, a, a, a, a) -> a
mediana (x,y,z,t,u)
 | x > z         = mediana (z,y,x,t,u)
 | y > z           = mediana (x,z,y,t,u)
 | z > u                = mediana (x,y,u,t,z)
 | z >t                 = mediana (x,y,u,z,t)
 | otherwise            = z



Tema 2 - Examples/Ejercicios

2. Define una función
            máximoYresto :: Ord a => [a] -> (a,[a])
que “devuelva” en forma de par el máximo de la lista y los restantes elementos. Considera dos casos:
      a.- el orden en que aparecen los restantes puede ser arbitrario:
             máximoYresto [1,2,30,4,5,6,7]   (30,[1,2,7,4,5,6])
      b.- los restantes deben aparecer en el orden original
             máximoYresto’ [1,2,30,4,5,6,7]  (30,[1,2,4,5,6,7])


maximoYresto :: Ord a => [a] -> (a,[a])
maximoYresto [] = (null,[])
maximoYresto (x:xs) = if(x>=head xs) then maximoYresto (x:tail xs)
                        else maximoYresto xs


8. Un número natural p es primo si tiene exactamente dos divisores positivos distintos: 1 y p; por
tanto, 1 no es un número primo.
        a) Define una función esPrimo para comprobar si un número es primo. Por ejemplo:
               esPrimo 7  True esPrimo 10  False
        b) Usando una lista por comprensión, define una función primosHasta que devuelva una lista
            con todos los números primos menores o iguales a un valor dado. Por ejemplo:
               primosHasta 50  [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
         c) Da otra definición (con nombre primosHasta') que use la función predefinida filter en vez
          de la lista por comprensión.

        d) Comprueba que las dos funciones que has definido se comportan del mismo modo
         comprobando con QuickCheck la siguiente propiedad:
                p1_primos x = primosHasta x == primosHasta' x
        Nota: existen métodos más eficientes para calcular listas de primos( Criba de  Eratóstenes.)



divisoresDe :: Integer -> [Integer]
divisoresDe n | n > 0       = [d | d <- [1..n], n `mod` d == 0]
              | otherwise   = error "no existen"
           

esPrimo :: Integer -> Bool
esPrimo  x = length (divisoresDe x) ==2

-- B) Define una lista por compresión
primosHasta :: Integer -> [Integer]
primosHasta  m  = [x | x<-[1..m],esPrimo (x)]

-- C) Misma función usando filter
primosHasta':: Integer -> [Integer]
primosHasta'  m = filter esPrimo [1..m]
-- D) quickCheck
p1_primos x = primosHasta x == primosHasta' x



14. La función predefinida
             takeWhile :: (a -> Bool) -> [a] -> [a]
devuelve el prefijo más largo con los elementos de una lista (2º argumento) que cumplen una
condición (1er argumento). Por ejemplo:
             takeWhile even [2,4,6,8,11,13,16,20]  [2,4,6,8]
ya que el 11 es el primer elemento que no es par. Otro ejemplo de uso es:
            takeWhile (<5) [2,4,6,1]  [2,4]
ya que 6 es el primer elemento de la lista mayor o igual a 5. Para los mismos argumentos, la función
dropWhile suprime el prefijo que takeWhile devuelve. Por ejemplo:
           dropWhile even [2,4,6,8,11,13,16,20]  [11,13,16,20]
           dropWhile (<5) [2,4,6,1]  [6,1]
a) Usando estas funciones, define una función inserta que tome un elemento x y una lista xs que ya
está ordenada ascendentemente (asume que esta precondición se cumple), y que devuelva la lista
ordenada que se obtiene al insertar x en su posición adecuada dentro de xs. Por ejemplo:
             inserta 5 [1,2,4,7,8,11 ]  [1,2,4,5,7,8,11]
             inserta 2 [1,2,4,7,8,11]  [1,2,2,4,7,8,11]
             inserta 0 [1,2,4,7,8,11]  [0,1,2,4,7,8,11]
             inserta 20 [1,2,4,7,8,11]  [1,2,4,7,8,11,20]
b) Sin usar ninguna función auxiliar, define directamente y en forma recursiva la función inserta.
c) Lee, entiende y comprueba con QuickCheck la siguiente propiedad referente a la función inserta:
             p1_inserta x xs = desconocida xs ==> desconocida (inserta x xs)
d) Podemos utilizar la función inserta que hemos definido para ordenar ascendentemente una lista
desordenada. Por ejemplo, si quisiéramos ordenar la lista [9,3,7], podríamos hacerlo evaluando
la expresión:
            9 `inserta` (3 `inserta` (7 `inserta` []))
Razona por qué funciona este algoritmo para ordenar una lista.
e) Usando la función foldr y la función inserta, define una función ordena que tome una lista de
valores y la devuelva ordenada. Por ejemplo:
            ordena [9,3,7]  [3,7,9] ordena "abracadabra" "aaaaabbcdrr"
f) Escribe y comprueba con QuickCheck la siguiente propiedad: para cualquier lista xs, ordena xs es
una lista ordenada.
g) Demuestra la propiedad anterior por inducción sobre listas.



desconocida :: (Ord a) => [a] -> Bool
desconocida xs = and [ x<=y | (x,y) <- zip xs (tail xs) ]


-- A)
inserta :: Ord a => a -> [a] -> [a]
inserta x xs = (takeWhile (<x) (xs)) ++ [x] ++ (dropWhile (<=x) (xs))


-- B) inserta directamente  y versión recursiva
inserta' :: Ord a => a -> [a] -> [a]
inserta' x [ ]  = [x]
inserta' x (y:ys) = [y] ++ inserta x (ys)

-- C)
p_inserta x xs = length xs <= 10 && desconocida xs ==> desconocida (inserta x xs)
p_inserta' x xs = length xs <= 10 &&  desconocida xs ==> desconocida (inserta' x xs)
pinserta = quickCheck  (p_inserta :: Int -> [Int] -> Property)
pinserta' = quickCheck (p_inserta' :: Int -> [Int] -> Property)

-- D) ¿por qué está ordenada la lista 9 `inserta` (3 `inserta` (7 `inserta` []))

-- E) algoritmo de ordenación a través de foldr e inserta
ordena :: Ord a => [a] -> [a]
ordena xs = foldr inserta [] xs


16. Aunque la función predefinida lcm (least common multiple) ya lo hace, el objetivo de este ejercicio es escribir una función mcm para calcular el mínimo común múltiplo de dos naturales.
       a) Define una función múltiplosDe, que tome como parámetro un número mayor que cero y
          devuelva una lista infinita con sus múltiplos positivos. Por ejemplo:
                múltiplosDe 3  [3,6,9,12,15,...
       Ayuda: usa la función predefinida iterate, descrita en un ejercicio anterior.
       b) Define la función sobrecargada para tipos con orden
               primeroComún :: Ord a => [a] -> [a] -> a
       que tome dos listas ordenadas ascendentemente y devuelva el menor elemento común a ambas.
       Por ejemplo:
               primeroComún [1,2,5,7,9] [3,3,4,5,7]  5
        c) El mínimo común múltiplo de dos naturales x e y es el menor natural positivo múltiplo de
        ambos. Utilizando las funciones definidas en los apartados previos, escribe una función mcm
        que calcule el mínimo común múltiplo de dos naturales usando esta definición.
        d) Comprueba usando QuickCheck tu definición mediante esta propiedad:
               p_mcm x y = x>=0 && y>=0 ==> mcm x y == lcm x y

-- A)
múltiplosDe :: Integer -> [Integer]
múltiplosDe x = map (*x) [1,2..]

-- B)
primeroComún :: Ord a => [a] -> [a] -> a
primeroComún              (x:xs) (y:ys)
  | x > y    = primeroComún (x:xs)    ys
  | x< y     = primeroComún (xs)    (y:ys)
  |otherwise = x

-- C)

 mcm de dos naturales como menor mútiplo común positivo
mcm x y = primeroComún (múltiplosDe x) (múltiplosDe y)
-- D)
p_mcm x y = x>=0 && y>=0 ==> mcm x y == lcm x y

Tema 3 - Examples/Ejercicios

1. Haskell. Escribe una función que permita determinar si una cadena de caracteres está bien
balanceada o equilibrada en lo que se refiere a los paréntesis, corchetes y llaves que contiene. El
resto de caracteres no interesan. Por ejemplo la cadena “v(hg(jij)hags{ss[dd]dd})” está
balanceada pero no así la cadena “ff(h([sds)sds]ss)hags”.

Para ello, se utilizará una pila en la que cada vez que aparezca un signo de apertura, se
introduce en la pila y cada vez que aparece un signo de cierre se extrae la cima de la pila y se
comprueba que corresponde al signo de apertura correspondiente. Si al finalizar de recorrer la
cadena la pila está vacía entonces la expresión está equilibrada.
   module WellBalanced where

   import DataStructures.Stack.LinearStack

    wellBalanced :: String -> Bool
   wellBalanced xs = wellBalanced' xs S.empty

   wellBalanced' :: String -> Stack Char -> Bool
   wellBalanced' [] s = isEmpty s
   wellBalanced' (x:xs) s ...
*WellBalanced > wellBalanced "vv(hg(jij)hags{ss[dd]dd})"
True




module WellBalanced where

import DataStructures.Stack.LinearStack

wellBalanced :: String -> Bool
wellBalanced xs = wellBalanced' xs empty

wellBalanced' :: String -> Stack Char -> Bool
wellBalanced' [] s = True
wellBalanced' (x:xs) s
   | isOpen x   = wellBalanced' xs (push x s)

   | isClosed x = if (match (top s) x) then wellBalanced' xs (pop s) else wellBalanced' (xs) s

   | otherwise  = wellBalanced' (xs) s

isOpen x = elem x "([{"
isClosed x = elem x ")]}"

match '(' ')' = True
match '[' ']' = True
match '{' '}' = True
match _ _ = False


11. Haskell. Un saco (o multiconjunto) es parecido a un conjunto salvo que un elemento puede estar
incluido varias veces. Por ejemplo, {‘b’, ‘a’, ‘d’, ‘d’, ‘a’, ‘c’ , b’, ‘a’} es un saco que
incluye tres ocurrencias del carácter ‘a’ , dos ocurrencias del ‘b’, una del ‘c’ y dos del ‘d’.
        a) Implementa sacos en Haskell usando el siguiente tipo de datos:
                   data Bag a = Empty | Node a Int (Bag a)
           de forma que en cada nodo aparece, además del saco restante, cada elemento junto con
           su contador de ocurrencias, o sea, el número de veces que aparece. Para agilizar
           las operaciones de inserción y borrado en un Bag, interesa que los nodos estén ordenados
           atendiendo al orden de los elementos a incluir. Además, no deben aparecer elementos con
           contador nulo (o negativo). Por ejemplo, el saco anterior se representa por:
                  Node ‘a’ 3 (Node ‘b’ 2 (Node ‘c’ 1 (Node ‘d’ 2 Empty)))
           La implementación debe incluir las siguientes funciones:
                  empty :: Bag a -- Devuelve un saco vacío
                  isEmpty :: Bag a -> Bool -- Comprueba si un saco está vacío
                  insert :: (Ord a) => a -> Bag a -> Bag a --Inserta una nueva ocurrencia
                 occurrences :: (Ord a) => a -> Bag a -> Int -- Devuelve el número de
                  -- ocurrencias de un elemento en un saco (0 si el elemento no está) -}
                 delete :: (Ord a) => a -> Bag a -> Bag a -- Borra una ocurrencia de un
                -- elemento de un saco. Devuelve el mismo saco si el elemento no estaba incluido
        b) Proporciona una especificación de Bag definiendo sus axiomas para las diferentes
         operaciones y comprueba la implementación realizada con QuickCheck. Para ello,
         incluye en el módulo la siguiente instancia para generar sacos aleatorios:
                  instance (Ord a, Arbitrary a) => Arbitrary (Bag a) where arbitrary = do
                       xs <- listOf arbitrary return (foldr insert empty xs)
        c) Añade al módulo las siguientes funciones para manipular sacos: unión, intersección,
        diferencia y una función que determine si un saco está contenido en otro. Estas
        funciones son semejantes a las de los conjuntos pero teniendo en cuenta las
        ocurrencias de cada elemento.
      d) Analiza la complejidad de las diferentes operaciones y justifica las ventajas de
       mantener los elementos ordenados.


module Bag
  ( Bag
  , empty
  , isEmpty
  , insert
  , delete
  , occurrences
  ) where

import Test.QuickCheck 

----------------
--- a)
----------------
data Bag a =  Empty | Node a Int  (Bag a) deriving Eq --Node tiene 3 argumentos: a, n (entero) y la bolsa

-- no deben aparecer nodos con el contador a cero.
-- es decir, los eliminamos
-- mantenemos los objetos ordenados
-- imponemos una relación de orden al tipo base.
-- esto permitirá comparar dos sacos: igualdad, pertenencia, etc.

empty :: Bag a            
empty = Empty

isEmpty :: Bag a -> Bool   
isEmpty Empty = True
isEmpty (Node a n s) = False

--'a' -> Empty
--'c' -> Nodo 'a' 1 Empty
--'a' -> Nodo 'a' 1 (Nodo 'c' 1 Empty)
--'c' -> Nodo 'a' 1 (Nodo 'c' 2 Empty)
--         Nodo 'a' 2 (Nodo 'c' 2 Empty)

--Las bolsas se expresan: Nodo 'a' 3 (Nodo 'c' 5 (Nodo 'd' 1 Empty) = 'a' aparece 3 veces, 'c' 5 veces y 'd' 1 vez
-- (a 3):((c 5):((d 2) : [ ])) = ':' es nodo y [ ] es Empty

insert :: (Ord a) => a -> Bag a -> Bag a   
-- Inserta una nueva ocurrencia en un saco
-- pedimos Ord para mantener el orden 
insert x Empty          =  Node x 1 Empty
insert x (Node y oy s) | x < y = Node x 1 (Node y oy s)
                                | x == y =  Node y (oy+1) s
                                | x > y =  Node y oy (insert x s)

occurrences :: (Ord a) => a -> Bag a -> Int    
-- Devuelve el número de ocurrencias de un elemento en un saco
-- 0 si el elemento no está
occurrences x Empty     = 0
occurrences x (Node y oy s) | x ==y = oy
                                          | x < y = 0
                                          | x > y = occurrences x s

delete :: (Ord a) => a -> Bag a -> Bag a       
-- Borra  una ocurrencia de un elemento de un saco.
-- Devuelve el mismo saco si el elemento no estaba incluido
delete x Empty          =  Empty
delete x (Node y oy s) | x == y = Node y (oy-1) s
                                 | x < y = Node y oy s
                                 | x > y = Node y oy (delete x s)

instance (Show a) => Show (Bag a) where
        show s = "Bag { " ++ show' s
          where
                show' Empty = "}"
                show' (Node x ox s) = muestra x ox ++  show' s   
                muestra x 0  = ""
                muestra x ox = show x ++ ' ':muestra x (ox-1)

----------------
--- b) Proporcionar una especificación de sacos definiendo sus axiomas
--     para las diferentes operaciones y comprobar la implementación
--     realizada con QuickCheck.
----------------
instance (Ord a, Arbitrary a) => Arbitrary (Bag a) where
  arbitrary =  do
                xs <- listOf arbitrary
                return (foldr insert empty xs)

-- Sobre insertar
ax1  x y s      =       insert x(insert y s) == insert y(insert x s)

-- Sobre ocurrences
ax2  x         =       empty
ax3 x y s     | x==y = occurrences x (insert y s) == 1 + occurrences x s
                  | x/=y = occurrences x (insert y s) == occurrences x s


-- Sobre delete
ax4 x           =       empty
ax5 x   s       =       delete x (insert x s) == s
ax6 x y s       =    x /= y  ==>   (delete x (insert y s) == insert y (delete x s))

-- Sobre isEmpty
ax7             =       isEmpty empty
ax8 x s         =      isEmpty (insert x s) == False

{- Si no tipificamos, solo genera pruebas con 'data () = ()'
*Bag> quickCheck ax6
*** Gave up! Passed only 0 tests.
*Bag> quickCheck (ax6 :: Char -> Char -> Bag Char -> Property)
+++ OK, passed 100 tests.
*Bag> quickCheck (ax6 (12::Integer))
+++ OK, passed 100 tests.
*Bag> quickCheck (ax6 'a')
+++ OK, passed 100 tests.
*Bag> quickCheck (ax5 'a')
+++ OK, passed 100 tests.
*Bag> quickCheck (ax5 True)
+++ OK, passed 100 tests.
-}


----------------
-- c)    Añadir al módulo las siguientes funciones para manipular sacos:
-- unión, intersección, diferencia y una función que determine si un saco
-- está contenido en otro.
-- Estas funciones son semejantes a las de conjunto pero teniendo
-- en cuenta las ocurrencias de cada elemento.
----------------
union :: Ord a => Bag a -> Bag a -> Bag a
-- queda curioso la unión al estilo de merge
union s     Empty = s
union Empty t     = t
union (Node x ox s) (Node y oy t)
      |x<y = (Node x ox (Node y oy (union s t)))
      |x>y = (Node y oy (Node x ox (union s t)))
      |x==y = Node x (ox + oy) (union s t)

intersection :: Ord a => Bag a -> Bag a -> Bag a
intersection s     Empty = Empty
intersection Empty t     =  Empty
intersection (Node x ox s) (Node y oy t)
      | x == y = Node x (ox+oy) (intersection s t)
      | x < y = intersection s (Node y oy t)
      | x > y = intersection (Node x ox s) t



difference  :: Ord a => Bag a -> Bag a -> Bag a
difference s     Empty =  s
difference Empty t     =  Empty
difference (Node x ox s) (Node y oy t)
      | x == y = difference s t
      | x < y = Node x ox (difference s (Node y oy t))
      | x > y = difference (Node x ox s) t


inBag :: Ord a => Bag a -> Bag a -> Bool
inBag    Empty t     =  True
inBag    t     Empty =  False
inBag   (Node x ox s) (Node y oy t)
      | x==y = inBag s t
      | x < y = False
      | x > y = inBag (Node x ox s) t

-- Ejercicio: Encontrar axiomas para estas operaciones

-- union
ax_union1  s t    = union s t == union t s
-- quickCheck (ax_union1 saco1)
-- union e insert
ax_union2  x s t  = union (insert x s) t == union (insert x t) s
-- quickCheck (ax_union2 3)
-- intersection
ax_intersection1  s t    = intersection s t == intersection t s
-- intersection y delete
ax_intersection2  x s t  = occurrences x s == occurrences x t ==> intersection(delete x s) (delete x t) == intersection(delete x t) (delete x s)
--
-- difference y empty
ax_difference1  s     =  difference s s == Empty
-- quickCheck (ax_difference1 :: Set Integer -> Bool)
-- difference, insert y empty
ax_difference3  x s     = difference s (insert x s) == Empty
-- quickCheck (ax_difference3 :: Int -> Bag Int -> Bool)
-- difference e insert
ax_difference4  x s t  = difference t (insert x s) == difference (insert x t) s
-- quickCheck (ax_difference4 :: Int -> Bag Int -> Bag Int -> Bool)
-- otra propiedad de los conjuntos que es falsa para Bag
-- difference y union
ax_difference2  s t u  = difference (union s t) u == union (difference s u) (difference t u)
-- quickCheck (ax_difference2 :: Bag Int -> Bag Int -> Bag Int -> Bool)

-- ATENCION: falla la distributiva
ax_uni_inter1  s t  u  = intersection (union s t) u  ==
                         union (intersection s u) (intersection t u)
--  *Bag> quickCheck (ax_uni_inter1)
--  *** Failed! Falsifiable (after 4 tests): 
--  Bag { () () }
--  Bag { () }
--  Bag { () }


------------
-- d)    Analizad la complejidad de las diferentes operaciones
-- y justificar las ventajas de mantener los elementos ordenados.
------------

--- Otras funciones

cardinal :: Ord a => Bag a -> Int
cardinal Empty = 0
cardinal (Node x ox s) = ox + cardinal s

foldBag :: ( a -> Int -> b -> b) -> b -> Bag a -> b
foldBag f z Empty         = z
foldBag f z (Node x ox s) = f x ox (foldBag f z s)
 
elemBag y =  foldBag esta False
   where esta x ox b = x==y && b

sumBag = foldBag sum3 0
  where sum3 x ox z = x*(fromIntegral ox) + z

cardinal' = foldBag (const (+) ) 0

-- Las ventajas de tener los elementos ordenados es que permite que se puede operar
--con las bolsas de una forma más rápida y además las funciones necesitan menos casos base. 


--------------------------------------------------------------------------------
Demas ejercicios de las relaciones de otros años
 --------------------------------------------------------------------------------

1. Consideremos la función:
        dosVeces :: (Integer -> Integer) -> Integer -> Integer
        dos Veces f x = f (f x)
    a) ¿Cuál es el tipo de la función: fun = dosVeces (+1)?
        fun :: Integer -> Integer
    b) Escribe una λ-expresión equivalente a la función fun
        fun’ :: Integer -> Integer
         fun’ = λx -> x + 2

    c) Escribe una sección equivalente a la función fun
        fun'’ :: Integer -> Integer
        fun’’ = (+2)

2. Escribe una función derivada que devuelva la derivada de una función de reales en reales
usando la aproximación:
Por ejemplo:
     coseno :: Float -> Float
     coseno= derivada sin
     MAIN> derivada sqrt 1.0
      > 0.499487 :: Float
     MAIN> coseno 0.0
     > 1.0 :: Float
¿Cuál es el tipo de la función derivada?
      derivada :: (Float -> Float) -> Float -> Float
      derivada f x = (f (x + epsilon) – f x) / épsilon
          where
               epsilon = 1e - 4

3. ¿Cuáles son los tipos polimórficos de las siguientes funciones?
         swap (x, y) = (y, x) (a,b) -> (b,a)
         const x y = x a -> b -> a
         subst f g x = f x (g x) (a -> b -> c) -> (a -> b) -> a -> c
         pair (f,g) x = (f x, g x) (a -> b, a -> c) -> a -> (b, c)
        cross (f,g) (x,y) = (f x, g y) (a -> b, c -> d) -> (a, c) -> (b, d)
        comp f g x = f (g x) (a -> b) -> (c -> a) -> c -> b
        fix f = f (fix f) (a -> a) -> a

4. ¿Qué hace el siguiente operador?
       infixr 0 >$>
       (>$>) :: (a -> a) -> a -> a
       f >$> x = f (f x)
         Aplica la función dos veces
¿Por qué su tipo no es (>$>) :: (a -> b) -> a -> b?
      La función f debe devolver un parámetro del mismo tipo que su parámetro de entrada
      porque el resultado de aplicar la primera vez la función será el parámetro de entrada
      en la segunda aplicación de la función

5. Define el operador (>$>) del ejercicio anterior usando la composición de funciones (.)
      infixr 0 >$> (>$>) :: (a->a) -> a -> a (>$>) f = f . f
6. Consideremos el siguiente operador:
      infixl 9 >.>f >.> g = λ x -> g (f x)
 ¿Cuál es su tipo polimórfico)
     (a -> b) -> (b -> c) -> a -> c
¿Qué hace la siguiente función?
     fun :: Integer -> Integer
     fun = (+2) >.> (*2) >.> (+1)
         Debido a la asociación a la izquierda del operador, sería como tener ((+2) >.>
         (*2)) >.> (+1). Dado un número natural, primero le suma 2, luego lo multiplica por 2, y
         luego le suma 1.

7. La función predefinida reverse invierte el orden de los elementos de una lista. Por ejemplo
      MAIN> reverse [1,2,3]
      [3,2,1] :: Integer
Defínela y da su tipo polimórfico. ¿Cuál es el tipo de la función palíndromo xs = (reverse xs ==
xs)?
      reverse :: [a] -> [a]
      reverse [] = []
      reverse (x:xs) = reverse xs ++ [x]
      palíndromo :: [a] -> Bool
      palíndromo xs = (reverse xs == xs)

8. ¿Cuál es el tipo polimórfico de la función twice?
     twice f x = f (f x)
      twice :: (a -> a) -> a -> a
¿Cuál es el valor de cada una de las siguintes expresiones?
    twice (+1) 0 2
     twice twice (+1) 0 4
Demostración:
    twice (+1) 0 -- f = (+1) ; x = 0
     => definición de twice
     (+1) ( (+1) 0 )
    => definición de la función (+1) (la más interna, pues es la única aplicable)
     (+1) 1
    => definición de la función (+1)
   2
   twice twice (+1) 0 -- f = twice (+1) ; x = 0
    => definición de twice (en el rédex más externo)
    twice (+1) (twice (+1) 0)
    => definición de twice (en el twice más interno)
    twice (+1) ( (+1) ( (+1) 0 ))
    => definición de la función (+1) (la más interna, pues es la única aplicable)
    twice (+1) ( (+1) 1 )
    => definición de la función (+1) (la más interna, pues es la única aplicable)
    twice (+1) 2
    => definición de la función twice
    (+1) ( (+1) 2 )
    => definición de la función (+1) (la más interna, pues es la única aplicable)
    (+1) 3
    => definición de la función (+1)
    4

9 La función predefinida zip empareja dos listas. Por ejemplo
    ? zip [1, 2, 3] [4, 5, 6]
    [(1, 4), (2, 5), (3, 6)] :: [(Integer , Integer )]
    ? zip [1, 2, 3] [True, False]
    [(1,True), (2, False)] :: [(Integer ,Bool )]
    ? zip [True, False] [1, 2, 3]
   [(True, 1), (False, 2)] :: [(Bool , Integer )]
    Defínela y da su tipo polimórfico
   zip :: [a] -> [b] -> [(a,b)]
   zip' (x:xs) (y:ys) = (x,y) : zip' xs ys
   zip' _ _ = []

10. ¿Cuál es el tipo de las siguientes expresiones (en caso de que sean correctas)?
     not . even Int -> Bool (Bool -> Bool) -> (Int -> Bool)
     even . not Incorrecto, ya que la función not produce un Booleano y
     la función even necesita como entrada un número
     chr . ord Char -> Char (Int -> Char) -> (Char -> Int)
    ord . chr Int -> Int (Char -> Int) -> (Int -> Char)
    ord . chr . (+1) Int -> Int (Char -> Int) -> ( (Int -> Char) -> (Int -> Int) )
    map not [Bool] -> [Bool]
    map (λ x -> not x ) [Bool] -> [Bool]
    map (not . even) [Int] -> [Bool]
    map not [True, False] [Bool]
    map ord [Char] -> [Int]
    map (+1) [a] -> [a], donde a es un número
    map (map (+1)) [[a]] -> [[a]], donde a es un número
    map (++[1]) [[a]] -> [[a]]
    map (1 :) [[a]] -> [[a]]
 Cuál es el valor de la expresión map (map (+1)) [[1, 2, 3], [10, 11]]?
    [[2,3,4], [10,11]]
    map (map (+1)) ([1,2,3] : [[10,11]]) = map (+1) [1,2,3] : map (map (+1)) [[10,11]]
 Primera parte:
    map (+1) [1,2,3] = [2,3,4]
Segunda parte:
    map (map (+1)) ([10,11] : []) = map (+1) [10,11] : map (map (+1)) []
Primera parte:
    map (+1) [10,11] = [11,12]
Segunda parte:
   map (map (+1)) [] = []

Uniendo todo, resulta:
   [2,3,4] : [11.12] : [] = [[2,3,4], [11,12]] 

Tema 4 - Teoria General

ARBOL NORMAL
Creacion de un Arbol normal:
          tree1= Node 1[ Node 2 [ Node 4 [],Node 5[], Node 6[]], Node 3 [ Node 7 [] ] ]
suma de los valores de los nodos
         sumT tree1
altura de un arbol
        heighT tree1

ARBOL BINARIO
creacion de un arbol binario
      tree2 =NodeB 1 (NodeB 2 (NodeB 4 EmptyB EmptyB) (NodeB 5 EmptyB EmptyB))
       (NodeB 3 (NodeB 6 EmptyB EmptyB) EmptyB)  
 Suma:
      sumB tree2 
 saber que hay a que nivel del arbol binario
      atLevelB 0 tree2
caminos hasta cierto nodo desde la raiz
      pathsToB  (nodo) (arbol)
      pathsToB  5  tree2
      preOrderB  tree2
      inOrderB tree2
      postOrderB tree2

construir un arbol binario de altura minima 
minTreeB' :: [a] -> TreeB a
minTreeB' xs = fst (aux (length xs) xs)
 

aux :: Int -> [a] -> (TreeB a, [a])
aux 0 xs = (EmptyB, xs)
aux 1 xs = (NodeB (head xs) EmptyB EmptyB, tail xs)
aux n xs = (NodeB y t1 t2, zs)
   where
      m = div n 2
      (t1, y:ys) = aux m xs
      (t2, zs) = aux (n-m-1) ys

 COLAS CON PRIORIDAD (SIFO)


definir una cola
      cola1 =enqueue 3  (enqueue 1  (enqueue 2 (empty)))
devuelve una cola vacía
      empty :: PQueue a
test para colas vacías
      isEmpty :: PQueue a -> Bool
inserta un elemento en una cola de prioridad
      enqueue :: (Ord a) => a -> PQueue a -> PQueue a
devuelve el elemento de mínima prioridad
      first :: (Ord a) => PQueue a -> a
devuelve la cola obtenida al eliminar el elemento de mínima prioridad
      dequeue :: (Ord a) => PQueue a -> PQueue a

MONTICULO -  HEAP ORDER PROPERTY EN JAVA

public BinaryHeap()
public int size()
public boolean isEmpty()
private boolean lessThan(int idx1, int idx2)
private void swap(int idx1, int idx2)
private boolean isRoot(int idx)
private int parent(int idx)
private int leftChild(int idx)
private int rightChild(int idx)
private boolean isNode(int idx)
private boolean hasLeftChild(int idx)
private boolean isLeaf(int idx)
public void insert(T x)
public T minElem()

ARBOLES BINARIOS DE BUSQUEDA - BINARY SEARCH TREE (BST) HASKELL





busqueda:
    search 5 tree2
es elemento:
    isElem 5 tree2
    isElem x t = isJust (search x t)
existe una funcion llamada Maybe, que si encuentra SOLO algo entonces asigna el valor TRUE

    data Maybe a = Nothing | Just a
    isJust :: Maybe a -> Bool
    -- si contiene algo entonces da true

    isJust (Just _) = True
    --si no contiene nada da false
    isJust Nothing = False  


Minimo elemento
       minim tree2
Maximo elemento
       maxim tree2
Quitar  y  devolver  el  mínimo  elemento  de  un  árbol:
      split tree2
Eliminar
      delete 4 tree2
Unir
      join tree1 tree2

ARBOLES BINARIOS DE BUSQUEDA - BINARY SEARCH TREE (BST) JAVA

public boolean isEmpty();
public int size();
public int height();
public void insert(K k, V v);
public V search(K k);
public boolean isElem(K k);
public void delete(K k);
public Iterable<K> inOrder();
public Iterable<K> postOrder();
public Iterable<K> preOrder();
public Iterable<V> values();
public Iterable<Tuple2<K,V>> keysValues();

ARBOLES AVL

son arboles que como mucho difieren en altura 1

AVL treeAVL = Nodo 9 (Nodo 3 (Nodo 2 Hoja Hoja) (Nodo 4 Hoja Hoja)) (Nodo 7 Hoja Hoja)

height treeAVL
isAVL treeAVL
rotR treeAVL
rotL treeAVL
insert 4 treeAVL

        inclinacion a la izquierda ,derecha y balanceando de nuevo
rightLeaning treeAVL
leftLeaning treeAVL 
balance altura parteIZQ parteDER
        busqueda
search 5 treeAVL
isElem 5 treeAVL
delete 5 treeAVL
join treeAVL1 treeAVL2
        elimina y devuelve el minimo elemento de un arbol
split treeAVL

ARBOLES AVL EN JAVA

public Tree(C key, D value)
isEmpty();
height(Tree<?,?> tree);
rightLeaning();
leftLeaning();
setHeight();
rotR();
rotL();
balanced();
search(K key);
isElem(K key);

DICCIONARIOS HASKELL

diccionario claves valores

diccionario = empty
isEmpty diccionario
insert clave valor diccionario
valueof valor diccionario



Tema 4 - Examples/Ejercicios


La biblioteca de Haskell (Haskell.zip) debe descomprimirse en un directorio y después desde WinGHCi hacer referencia a ella a través de la orden:

Prelude> :set -idirectorioDondeSeEncuentraLaBiblioteca

Por ejemplo, si descomprimes Haskell.zip en el directorio de trabajo donde están la plantilla y el programa de prueba, la orden anterior es:

EJERCICIO MONTICULOS MAXIFOBICOS HASKELL
Este ejercicio está tomado de http://programmingpraxis.com.

Los montículos Maxifóbicos (Maxophobics) son una variante de montículos zurdos, y ambos
permiten una implementación eficiente de una cola de prioridad.
En los zurdos, el peso del hijo izquierdo es siempre mayor o igual que el del derecho.
Como los zurdos, los maxifóbicos se representan por medio de árboles binarios aumentados, que
mantienen la propiedad de Heap Order, y por tanto, en cada nodo la clave es menor que las claves
de sus dos hijos. Por tanto el minElem se encuentra en la raíz del árbol. delMin descarta la raíz y
mezcla sus dos hijos e insert mezcla el árbol existente con un nuevo árbol con el elemento a
insertar como único elemento (singleton).
Los árboles zurdos son simples de utilizar y de codificar; es fácil demostrar que la mezcla de dos
montículos zurdos (resuelto en las transparencias) es zurdo, y de esta forma la longitud de su espina
derecha no supera al logaritmo del total de nodos de la mezcla, siendo ésta, salvo un factor de
proporcionalidad, una cota del número de operaciones de la mezcla. De aquí se deduce que las
operaciones de inserción y eliminación tienen complejidad logarítmica, ya que éstas se reducen a
una mezcla.

Los montículos maxifóbicos son una alternativa a los zurdos, pero no necesitan estar balanceados a
la izquierda (lo que libera del uso del invariante de los zurdos), y sin embargo las operaciones de
inserción y borrado tienen complejidad logarítmica. La diferencia de los montículos zurdos con los
maxifóbicos se encuentra en la operación de mezclar. En los maxifóbicos, la operación de mezcla se
implementa comparando los valores de las raíces de los dos árboles. La menor se mantiene como
valor de la raíz del montículo hm combinado resultado de la mezcla; el resto de la información se
distribuye en tres árboles: el montículo ganador de la comparación (que contendrá la clave mayor),
y los hijos del perdedor. De estos tres árboles se toma el mayor (con más nodos) y se coloca en la
rama derecha del montículo mezcla hm, colocándose en la rama izquierda la mezcla de los dos
restantes. De esta forma el montículo combinado contiene todas las claves de los originales y
además verifica la propiedad HO (heap order). Sorprendentemente un sencillo razonamiento
permite demostrar que esta mezcla tiene complejidad logarítmica (véase el artículo de Chris
Okasaki, “Alternatives to Two Classic Data Structures”, SIGCSE’05, February 23–27, 2005).
El nombre maxifóbico quiere decir “esquivando (o descartando) el mayor”: se mezclan los dos
menores subárboles.

a) Completa la implementación de las operaciones de estos montículos maxifóbicos, modificando lo
necesario en el código de los árboles zurdos (en DataStructures.Heap.MaxiphobicHeap).

b) (Difícil) La operación de mezcla de montículos maxifóbicos normalmente se implementa con
complejidad logarítmica por lo que es una implementación eficiente. Probar que efectivamente la
operación tiene complejidad logarítmica.

Ayuda: Sea T(N) el número de invocaciones a la función de mezcla para mezclar dos árboles
maxifóbicos cuyo número total de nodos de los dos subárboles es N. Intenta escribir una relación
de recurrencia para T(N) teniendo en cuenta que el mayor de los subárboles se ha evitado en cada
paso. Para calcular el tamaño total de los dos subárboles que se mezclarán (los dos más pequeños),
se debería primeramente determinar cuantos elementos tiene el subárbol evitado (el mayor).

c) Si interpretamos el tamaño (size) de un montículo maxifóbico como la altura del árbol, modifica el
código y estudia la complejidad de la operación de mezcla.

module MaxiphobicHeap
  ( Heap
  , empty
  , isEmpty
  , minElem
  , delMin
  , insert   -- puede definirse via merge
  , merge
-- los siguientes son auxiliares
  , mkHeap
--  , isHeap -- si exportamos leftChild, rightChild puede ser auxiliar
--  , verifyOP -- si exportamos leftChild, rightChild puede ser auxiliar
--  , drawOnWith
  ) where

-- import DrawTrees
import Test.QuickCheck

data Heap a = Empty | Node a Int (Heap a) (Heap a) deriving Show

-- number of elements
size :: Heap a -> Int
size Empty            = 0
size (Node _ sz _ _)  = sz

empty :: Heap a
empty  = Empty

isEmpty :: Heap a -> Bool
isEmpty Empty  = True
isEmpty _      = False

singleton :: a -> Heap a
singleton x  = Node x 1 Empty Empty

insert :: (Ord a) => a -> Heap a -> Heap a
insert x h  = merge (singleton x) h

minElem :: Heap a -> a
minElem Empty           = error "minElem on empty heap"
minElem (Node x _ _ _)  = x

delMin :: (Ord a) => Heap a -> Heap a
delMin Empty             = error "delMin on empty heap"
delMin (Node _ _ lh rh)  = merge lh rh

----------------------------------------------------------
---------------------------------------------------------
-- recursively merges smallest subheaps. Achieves O(log n) complexity
merge :: (Ord a) => Heap a -> Heap a -> Heap a
merge Empty h'     = h'
merge h     Empty  = h
merge h@(Node x sz lh rh) h'@(Node x' sz' lh' rh')
 | x < x'          = Node x (sz+sz') smaller1 smaller2
 | otherwise       = merge h' h
 where
  smaller1
    | ((size lh <= size rh)&&(size lh <= sz')) = lh
    | ((size rh <= size lh)&&(size rh <= sz')) = rh
    | otherwise = h'
  smaller2
     | ((size lh <= size rh)&&(size lh <= sz')) = merge rh h'
    | ((size rh <= size lh)&&(size rh <= sz')) = merge lh h'
    | otherwise = merge lh rh
----------------------------------------------------------
----------------------------------------------------------

-- Efficient O(n) bottom-up construction for heaps
mkHeap :: (Ord a) => [a] -> Heap a
mkHeap []  = empty
mkHeap xs  = mergeLoop (map singleton xs)
  where
    mergeLoop [h]  = h
    mergeLoop hs   = mergeLoop (mergePairs hs)

    mergePairs []         = []
    mergePairs [h]        = [h]
    mergePairs (h:h':hs)  = merge h h' : mergePairs hs
{-
-------------------------------------------------------------------------------
-- Generating arbitrary Heaps
-------------------------------------------------------------------------------
instance (Ord a, Arbitrary a) => Arbitrary (Heap a) where
  arbitrary  = do
    xs <- arbitrary
    return (mkHeap xs)

-------------------------------------------------------------------------------
-- Invariants
-------------------------------------------------------------------------------
verifyOP :: (Ord a) => Heap a -> Bool
verifyOP Empty             = True
verifyOP (Node x _ lh rh)  = x `lessEq` lh && x `lessEq` rh
                           && verifyOP lh && verifyOP rh
 where
  x `lessEq` Empty            = True
  x `lessEq` (Node x' _ _ _)  = x<=x'
-------------------------------------------------------------------------------
-- Drawing a Heap
-------------------------------------------------------------------------------
instance Subtrees (Heap a) where
  subtrees Empty             = []
  subtrees (Node _ _ lh rh)  = [lh,rh] 

instance (Show a) => ShowNode (Heap a) where
  showNode (Node x _ _ _) = show x

drawOnWith :: FilePath -> (a -> String) -> Heap a -> IO ()
drawOnWith file toString = _drawOnWith file showHeap
 where
  showHeap (Node x _ _ _) = toString x
-}


----------------------------------------------------------------------------------------
------------------------------------para probar--------------------------------------
---------------------------------------------------------------------------------------- 

module MaxiphobicHeapDemos where

import Data.List(nub)
import MaxiphobicHeap
import DataStructures.Random
import DrawTrees

drawHeap :: (Show a) => Heap a -> IO () 
drawHeap = drawOn "MaxiphobicHeap.png"

outlineHeap :: Heap a -> IO () 
outlineHeap = outlineOn "MaxiphobicHeap.png"

drawCharHeap :: String -> IO ()
drawCharHeap xs = drawOnWith "MaxiphobicHeap.png" (\k -> [k]) (mkHeap xs)

drawIntHeap :: Heap Int -> IO ()
drawIntHeap h = drawOnWith "MaxiphobicHeap.png" (\k -> show k) h

randomHeap :: Int -> Seed -> Heap Int
randomHeap sz seed = mkHeap (take sz . nub . randoms $ seed)

randomHeapI (a,b) sz seed = mkHeap (take sz . nub . randomsR (a,b) $ seed)
-- take 10 $ randomsR (1,10) 32

demo1 sz seed = outlineHeap (randomHeap sz seed)

demoI (a,b) sz seed = drawIntHeap (randomHeapI (a,b) sz seed)

demo2 xs = drawHeap (mkHeap xs)

demo3 = drawCharHeap "murcielago"

{-

h1 = foldl (flip insert) empty [4,2,3,1,2,4,7,1]


instance (Ord a, Arbitrary a) => Arbitrary (Heap a) where
  arbitrary = do
    vs <- arbitrary
    return (foldr insert empty vs)

isSorted :: (Ord a) => [a] -> Bool
isSorted []       =  True
isSorted [x]      =  True
isSorted (x:y:zs) =  x<=y && isSorted (y:zs)

p1 xs = isSorted ys && null (xs\\ys) && null(ys\\xs)
 where
  h = toHeap xs
  ys = toList h

toHeap xs = foldr insert empty xs

toList h
 | isEmpty  h = []
 | otherwise  = minElem h : toList (delMin h)
-}

MISMO EJERCICIO EN JAVA

Implementa los montículos maxifóbicos en Java.
(dataStructures.heap.MaxiphobicHeap.java)

/**
 * Binary Search trees implementation
 */

 package tree;

import java.util.Iterator;
import java.util.NoSuchElementException;
import stack.Stack;
import stack.StackLink;

public class BST<K extends Comparable<? super K>, V> implements Iterable<K> {
protected static class Tree<K, V> { protected K key; protected V value; protected Tree<K, V> left; protected Tree<K, V> right;
public Tree(K k, V v) { key = k; value = v; left = null; right = null; } }
private Tree<K, V> root;
public BST() { root = null; }
public boolean isEmpty() { return root == null; }
public void insert(K k, V v) { root = BST.insertRec(root, k, v); }
// returns modified tree private static <K extends Comparable<? super K>,V> Tree<K, V> insertRec(Tree<K, V> node, K key, V value) { if (node == null) node = new Tree<K, V>(key, value); else if (key.compareTo(node.key) == 0) node.value = value; else if (key.compareTo(node.key) < 0) node.left = insertRec(node.left, key, value); else node.right = insertRec(node.right, key, value); return node; }
public V search(K key) { return BST.searchRec(root, key); }
private static <K extends Comparable<? super K>,V> V searchRec(Tree<K, V> tree, K key) { if (tree == null) return null; else if (key.compareTo(tree.key) == 0) return tree.value; else if (key.compareTo(tree.key) < 0) return searchRec(tree.left, key); else return searchRec(tree.right, key); }
public boolean isElem(K key) { return search(key) != null; }
// pre: node is a non-empty tree // Removes minimum key (and value) from tree rooted at node. Before // deletion, key and value are saved into temp node. // returns modified tree (without min key and value) private static <K,V> Tree<K, V> split(Tree<K, V> node, Tree<K, V> temp) { if (node.left == null) { // min node found, so copy min key and value temp.key = node.key; temp.value = node.value; return node.right; // remove node } else { // remove min from left subtree node.left = split(node.left, temp); return node; } }
public void delete(K key) { root = BST.deleteRec(root, key); }
// returns modified tree private static <K extends Comparable<? super K>,V> Tree<K, V> deleteRec(Tree<K, V> node, K key) { if (node == null) ; // key not found; do nothing else if (key.compareTo(node.key) == 0) { if (node.left == null) node = node.right; else if (node.right == null) node = node.left; else /* * Tuple3<K,V,Tree<K,V>> tuple = split(node.right); node.key = * tuple._1(); node.value = tuple._2(); node.right = tuple._3(); */ node.right = split(node.right, node); } else if (key.compareTo(node.key) < 0) node.left = deleteRec(node.left, key); else node.right = deleteRec(node.right, key); return node; } // iterators
private abstract class Traversal implements Iterator<K> { Stack<K> stack = new StackLink<K>();
abstract void save(Tree<K, V> node);
public Traversal() { if (root != null) save(root); }
public boolean hasNext() { return !stack.isEmpty(); }
public K next() { if (!hasNext()) throw new NoSuchElementException();
K either = stack.top(); stack.pop(); return either; }
public void remove() { throw new UnsupportedOperationException(); }
}
public Iterator<K> inOrder() { return new Traversal() { void save(Tree<K, V> node) { // in reverse order, cause stack is LIFO if (node.right != null) save(node.right); stack.push(node.key); if (node.left != null) save(node.left); } }; }
public Iterator<K> postOrder() { return new Traversal() { void save(Tree<K, V> node) { // in reverse order, cause stack is LIFO stack.push(node.key); if (node.right != null) save(node.right); if (node.left != null) save(node.left); } }; }
public Iterator<K> preOrder() { return new Traversal() { void save(Tree<K, V> node) { // in reverse order, cause stack is LIFO if (node.right != null) save(node.right); if (node.left != null) save(node.left); stack.push(node.key); } }; }
public Iterator<K> iterator() { return inOrder(); }
}




 BST

data TreeB a = EmptyB | NodeB a (TreeB a) (TreeB a) deriving Show
 
preOrderB :: TreeB a -> [a]
preOrderB EmptyB          = []
preOrderB (NodeB x lt rt) = [x] ++ preOrderB lt ++ preOrderB rt

inOrderB :: TreeB a -> [a]
inOrderB EmptyB          = []
inOrderB (NodeB x lt rt) = inOrderB lt ++ [x] ++ inOrderB rt

postOrderB :: TreeB a -> [a]
postOrderB EmptyB          = []
postOrderB (NodeB x lt rt) = postOrderB lt ++ postOrderB rt ++ [x] 

-- crea un arbol binario conocido su recorrido en preorden y enorden
-- findTreeB [2,1,3,4,6] [3,1,4,2,6]  
-- ----> NodeB 2 (NodeB 1 (NodeB 3 EmptyB EmptyB) (NodeB 4 EmptyB EmptyB)) (NodeB 6 EmptyB EmptyB)
findTreeB :: Eq a => [a] -> [a] -> TreeB a
findTreeB [] _ = EmptyB
findTreeB (x:xs) ys = NodeB x (findTreeB is'  is) (findTreeB ds'  ds)
                where
                    (is,ds) = span (/=x) ys --is es la rama izquierda y ds es la rama derecha
                    nts = length is
                    (is',ds') = splitAt nts xs
                   

--span :: (a->Bool)->[a] -> ([a],[a]) Mientras se cumpla el predicado, va entrando elementos en la lista
--splitAt :: Int -> [a] -> ([a],[a]) Parte la lista


----------------------------------------------
JAVA
------------------------------------------------

/**
 * Binary Search trees implementation
 */

 package tree;

import java.util.Iterator;
import java.util.NoSuchElementException;
import stack.Stack;
import stack.StackLink;

public class BST<K extends Comparable<? super K>, V> implements Iterable<K> {
protected static class Tree<K, V> {
protected K key;
protected V value;
protected Tree<K, V> left;
protected Tree<K, V> right;

public Tree(K k, V v) {
key = k;
value = v;
left = null;
right = null;
}
}

private Tree<K, V> root;

public BST() {
root = null;
}

public boolean isEmpty() {
return root == null;
}

public void insert(K k, V v) {
root = BST.insertRec(root, k, v);
}

// returns modified tree
private static <K extends Comparable<? super K>,V> Tree<K, V> insertRec(Tree<K, V> node, K key, V value) {
if (node == null)
node = new Tree<K, V>(key, value);
else if (key.compareTo(node.key) == 0)
node.value = value;
else if (key.compareTo(node.key) < 0)
node.left = insertRec(node.left, key, value);
else
node.right = insertRec(node.right, key, value);
return node;
}

public V search(K key) {
return BST.searchRec(root, key);
}

private static <K extends Comparable<? super K>,V> V searchRec(Tree<K, V> tree, K key) {
if (tree == null)
return null;
else if (key.compareTo(tree.key) == 0)
return tree.value;
else if (key.compareTo(tree.key) < 0)
return searchRec(tree.left, key);
else
return searchRec(tree.right, key);
}

public boolean isElem(K key) {
return search(key) != null;
}

// pre: node is a non-empty tree
// Removes minimum key (and value) from tree rooted at node. Before
// deletion, key and value are saved into temp node.
// returns modified tree (without min key and value)
private static <K,V> Tree<K, V> split(Tree<K, V> node, Tree<K, V> temp) {
if (node.left == null) {
// min node found, so copy min key and value
temp.key = node.key;
temp.value = node.value;
return node.right; // remove node
} else {
// remove min from left subtree
node.left = split(node.left, temp);
return node;
}
}

public void delete(K key) {
root = BST.deleteRec(root, key);
}

// returns modified tree
private static <K extends Comparable<? super K>,V> Tree<K, V> deleteRec(Tree<K, V> node, K key) {
if (node == null)
; // key not found; do nothing
else if (key.compareTo(node.key) == 0) {
if (node.left == null)
node = node.right;
else if (node.right == null)
node = node.left;
else
/*
* Tuple3<K,V,Tree<K,V>> tuple = split(node.right); node.key =
* tuple._1(); node.value = tuple._2(); node.right = tuple._3();
*/
node.right = split(node.right, node);
} else if (key.compareTo(node.key) < 0)
node.left = deleteRec(node.left, key);
else
node.right = deleteRec(node.right, key);
return node;
}


// iterators

private abstract class Traversal implements Iterator<K> {
Stack<Either<K, Tree<K, V>>> stack = new StackLink<Either<K, Tree<K, V>>>();

abstract void save(Tree<K, V> node);

public Traversal() {
if (root != null)
save(root);
}

public boolean hasNext() {
return !stack.isEmpty();
}

public K next() {
if (!hasNext())
throw new NoSuchElementException();

Either<K, Tree<K, V>> either = stack.top();
stack.pop();

while (either.isRight()) {
Tree<K, V> node = either.right();
save(node);
either = stack.top();
stack.pop();
}
return either.left();
}

public void remove() {
throw new UnsupportedOperationException();
}

}

public Iterator<K> inOrder() {
return new Traversal() {
void save(Tree<K, V> node) {
// in reverse order, cause stack is LIFO
if (node.right != null)
stack.push(new Right<K, Tree<K, V>>(node.right));
stack.push(new Left<K, Tree<K, V>>(node.key));
if (node.left != null)
stack.push(new Right<K, Tree<K, V>>(node.left));
}
};
}

public Iterator<K> postOrder() {
return new Traversal() {
void save(Tree<K, V> node) {
// in reverse order, cause stack is LIFO
stack.push(new Left<K, Tree<K, V>>(node.key));
if (node.right != null)
stack.push(new Right<K, Tree<K, V>>(node.right));
if (node.left != null)
stack.push(new Right<K, Tree<K, V>>(node.left));
}
};
}

public Iterator<K> preOrder() {
return new Traversal() {
void save(Tree<K, V> node) {
// in reverse order, cause stack is LIFO
if (node.right != null)
stack.push(new Right<K, Tree<K, V>>(node.right));
if (node.left != null)
stack.push(new Right<K, Tree<K, V>>(node.left));
stack.push(new Left<K, Tree<K, V>>(node.key));
}
};
}

public Iterator<K> iterator() {
return inOrder();
}

}