viernes, 14 de febrero de 2014

Ejercicios de Introducción a Haskell

Aquí unos ejercicios básicos para empezar a entender Haskell. Con ellos empezamos a conocer sus tipos básicos, la definición de funciones y operadores, el manejo de tuplas, las pruebas con QuickCheck... ¡Importante importar Test.QuickCheck para realizar esto último! (:

{-
-- Ejercicio 1
Tres enteros positivos x, y, z constituyen una terna pitagórica si x^2+y^2=z^2, 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
-}


esTerna :: Integer -> Integer -> Integer -> Bool
esTerna x y z
        | (x^2 +y^2 == z^2 )= True
        | (x^2 + z^2 == y^2) = True
        | (y^2 + z^2 == x^2) = True
        | otherwise = 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
-}
terna :: Integer -> Integer -> (Integer, Integer, Integer)
terna x y= (x^2 - y^2, 2*x*y, x^2 + y^2)

{-
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

{-
--------------------------------------------------------
 Ejercicio 2

Define una función polimórfica
intercambia :: (a,b) -> (b,a)
que intercambie de posición los datos de la tupla:
--------------------------------------------------------
-}

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

{-
---------------------------------------------------------
Ejercicio 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:
-}
ordena2 :: Ord a => (a,a) -> (a,a)
ordena2 (x,y)
        | x > y = (y,x)
        | otherwise = (x,y)

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)
{-
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:
-}

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)
<br />
-- c) Escribe propiedades análogas a las del apartado anterior pero para esta función, y compruébalas usando QuickCheck.

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


p2_ordena3 x y z = mismosElementos (x,y,z) (ordena3 (x,y,z))
  where mismosElementos (x,y,z) (a,b,c) = (x,y,z) == (a,b,c) ||   (x,y,z) == (a,c,b) ||  (x,y,z) == (b,a,c) ||  (x,y,z) == (b,c,a) || (x,y,z) == (c,a,b) ||  (x,y,z) == (c,b,a)

{-
----------------------------------------------------------
Ejercicio 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
-}

max2 :: Ord a => a -> a -> a
max2 x y
    | x > y = x
    | otherwise = y
             
     
--b) Define las siguientes propiedades que debería verificar tu función max2 y compruébalas con QuickCheck

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


{-
----------------------------------------------------------
Ejercicio 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,
devolviendo True o False según corresponda.
----------------------------------------------------------
-}

entre :: Ord a => a -> (a,a) -> Bool
entre x (i1, i2) = x >= i1 && x <= i2

{-
----------------------------------------------------------
Ejercicio 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.
----------------------------------------------------------
-}
iguales3 :: Eq a => (a,a,a) -> Bool
iguales3 (x,y,z) = (x == y && x == z)

{-
---------------------------------------------------------
Ejercicio 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.
-}
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

--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

-----------------------------------------------------------
-- Ejercicio 8
-----------------------------------------------------------
unEuro :: Double
unEuro = 166.386

-- a) Define una función pesetasAEuros que convierta una cantidad (de tipo Double) de pesetas en los correspondientes euros.
pesetasAEuros :: Double -> Double
pesetasAEuros p = p/unEuro

-- b) b) Define la función eurosAPesetas que convierta euros en pesetas.
eurosAPesetas :: Double -> Double
eurosAPesetas e = e* unEuro


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

p_inversas x = eurosAPesetas (pesetasAEuros x) ~= x

{-
-----------------------------------------------------------
-- Ejercicio 10
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 b^2-4ac y que la ecuación tiene raíces reales si el discriminante no es negativo.
-----------------------------------------------------------
-}

raíces :: (Ord a, Floating a) => a -> a -> a -> (a, a)
raíces a b c
  | dis < 0     = error "Raíces no reales"
  | otherwise   = ((-b + raizDisc) / denominador, (-b - raizDisc) / denominador)
 where  dis     = b^2 - 4*a*c
        raizDisc = sqrt dis
        denominador = 2*a

p2_raíces a b c  = a/= 0 && b^2 - 4*a*c >= 0   ==> esRaíz r1 && esRaíz r2
  where
   (r1,r2) = raíces a b c
   esRaíz r = a*r^2 + b*r + c ~= 0

------------------------------------------------------------
-- Ejercicio 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.
-------------------------------------------------------------

esMúltiplo :: Integer -> Integer -> Bool
esMúltiplo x y = (mod x y == 0)

------------------------------------------------------------
-- Ejercicio 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
------------------------------------------------------------

infixl 1 ==>>
(==>>) :: Bool -> Bool -> Bool

False ==>> y    =       True
True ==>>  y     =       y

{-
------------------------------------------------------------
-- Ejercicio 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.
------------------------------------------------------------
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 :: Integer -> Bool
esBisiesto x
  | (mod x 4 == 0) &&  ((mod x 100==0) ==>> (mod x 400 == 0)) = True
  | otherwise = False

{-
------------------------------------------------------------
Ejercicio 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 b^n = b·b^(n-1) define una función recursiva potencia que tome un entero b y un exponente natural n y devuelva b^n.
-}
potencia :: Integer -> Integer -> Integer
potencia x n
  | n == 0      =       1
  | n > 0       =       x * potencia x (n-1)
  | otherwise   = error "Exponente negativo"

-- b)
potencia' :: Integer -> Integer -> Integer
potencia' x n
  | n == 0              =       1
  | even n         = potencia' x (div n 2) * potencia' x (div n 2)
  | otherwise       = x * potencia' x (div (n-1) 2) * potencia' x (div (n-1) 2)
-- 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

{-
--------------------------------------------------------------
-- Ejercicio 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.
--------------------------------------------------------------
-}
factorial :: Integer -> Integer
factorial 1 = 1
factorial n = n* factorial(n-1)

--------------------------------------------------------------
-- Ejercicio 16 -- FALTA APARTADO C
-- 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.
-}
divideA :: Integer -> Integer -> Bool
divideA a b = (mod b a == 0)

-- b)
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.
p2_divideA a b c = a /= 0 && divideA a b && divideA a c ==> divideA a (b+c)


{-
-------------------------------------------------------------
-- Ejercicio 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:
-------------------------------------------------------------
-}

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)
  | t < z = mediana (x, y, t, z, u)
  | u < z = mediana (x, y, u, t ,z)
  | otherwise = z


No hay comentarios:

Publicar un comentario