Tema 9: Declaraciones de tipos y clases

Librerías auxiliares

import Test.QuickCheck
import Data.List (nub)

1 Declaraciones de tipos

Declaraciones de tipos como sinónimos

type String = [Char]  

Declaraciones de tipos nuevos

type Pos = (Int,Int)
origen :: Pos
origen = (0,0)
izquierda (3,5)  ==  (2,5)  
izquierda :: Pos -> Pos
izquierda (x,y) = (x-1,y)  

Declaraciones de tipos parametrizadas

type Par a = (a,a)  
multiplica (2,5)  ==  10  
multiplica :: Par Int -> Int
multiplica (x,y) = x*y
copia 5  ==  (5,5)  
copia :: a -> Par a
copia x = (x,x)

Declaraciones anidadas de tipos

Las declaraciones de tipos pueden anidarse. Por ejemplo,

type Pos = (Int,Int)  
type Movimiento = Pos -> Pos  
type Arbol = (Int,[Arbol])  
Cycle in type synonym declarations  

2 Definiciones de tipos de datos

Definición de tipos con data

data Bool = False | True  

Uso de los valores de los tipos definidos

data Mov = Izquierda | Derecha | Arriba | Abajo  
movimiento Arriba (2,5)  == (2,6)
movimiento :: Mov -> Pos -> Pos
movimiento Izquierda (x,y) = (x-1,y)
movimiento Derecha   (x,y) = (x+1,y)
movimiento Arriba    (x,y) = (x,y+1)
movimiento Abajo     (x,y) = (x,y-1)
movimientos [Arriba, Izquierda] (2,5)  ==  (1,6)  
movimientos :: [Mov] -> Pos -> Pos
movimientos []     p = p
movimientos (m:ms) p = movimientos ms (movimiento m p)
movimiento (opuesto Arriba) (2,5)  == (2,4)
opuesto :: Mov -> Mov
opuesto Izquierda = Derecha
opuesto Derecha   = Izquierda
opuesto Arriba    = Abajo
opuesto Abajo     = Arriba

Definición de tipo con constructores con parámetros

data Figura = Circulo Float | Rect Float Float
ghci> :type Circulo
Circulo :: Float -> Figura
ghci> :type Rect
Rect :: Float -> Float -> Figura
cuadrado :: Float -> Figura
cuadrado n = Rect n n
area (Circulo 1)   ==  3.1415927
area (Circulo 2)   ==  12.566371
area (Rect 2 5)    ==  10.0
area (cuadrado 3)  ==  9.0
area :: Figura -> Float
area (Circulo r) = pi*r^2
area (Rect x y)  = x*y

Definición de tipos con parámetros

data Maybe a = Nothing | Just a  
divisionSegura 6 3  ==  Just 2
divisionSegura 6 0  ==  Nothing
divisionSegura :: Int -> Int -> Maybe Int
divisionSegura _ 0 = Nothing
divisionSegura m n = Just (m `div` n)
headSegura [2,3,5]  ==  Just 2
headSegura []       ==  Nothing
headSegura :: [a] -> Maybe a
headSegura [] = Nothing
headSegura xs = Just (head xs)

3 Definición de tipos recursivos

Definición de tipos recursivos: Los naturales

data Nat = Cero | Suc Nat
           deriving Show
ghci> :type Cero
Cero :: Nat
ghci> :type Suc
Suc :: Nat -> Nat
Cero
Suc Cero
Suc (Suc Cero)  
Suc (Suc (Suc Cero))

Definiciones con tipos recursivos

nat2int (Suc (Suc (Suc Cero)))  ==  3
nat2int :: Nat -> Int
nat2int Cero    = 0
nat2int (Suc n) = 1 + nat2int n
int2nat 3  ==  Suc (Suc (Suc Cero))  
int2nat :: Int -> Nat
int2nat 0 = Cero
int2nat n = Suc (int2nat (n-1))
ghci> suma (Suc (Suc Cero)) (Suc Cero)  
Suc (Suc (Suc Cero))  
suma :: Nat -> Nat -> Nat
suma Cero    n = n
suma (Suc m) n = Suc (suma m n)
suma (Suc (Suc Cero)) (Suc Cero)
= Suc (suma (Suc Cero) (Suc Cero))  
= Suc (Suc (suma Cero (Suc Cero)))  
= Suc (Suc (Suc Cero))  

Tipo recursivo con parámetro: Las listas

data Lista a = Nil | Cons a (Lista a)
longitud (Cons 2 (Cons 3 (Cons 5 Nil)))  ==  3  
longitud :: Lista a -> Int
longitud Nil         = 0
longitud (Cons _ xs) = 1 + longitud xs

Definición de tipos recursivos: Los árboles binarios

     5 
    / \
   /   \
  3     7
 / \   / \  
1   4 6   9  
data Arbol = Hoja Int | Nodo Arbol Int Arbol
ejArbol = Nodo (Nodo (Hoja 1) 3 (Hoja 4)) 
               5 
               (Nodo (Hoja 6) 7 (Hoja 9))

Definiciones sobre árboles binarios

ocurre 4  ejArbol  ==  True
ocurre 10 ejArbol  ==  False
ocurre :: Int -> Arbol -> Bool
ocurre m (Hoja n)     = m == n
ocurre m (Nodo i n d) = m == n || ocurre m i || ocurre m d
aplana ejArbol  ==  [1,3,4,5,6,7,9]  
aplana :: Arbol -> [Int]
aplana (Hoja n)     = [n]
aplana (Nodo i n d) = aplana i ++ [n] ++ aplana d

Definiciones sobre árboles binarios

ocurreEnArbolOrdenado 4  ejArbol  ==  True
ocurreEnArbolOrdenado 10 ejArbol  ==  False
ocurreEnArbolOrdenado :: Int -> Arbol -> Bool
ocurreEnArbolOrdenado m (Hoja n)  =  m == n
ocurreEnArbolOrdenado m (Nodo i n d)
     | m == n      =  True
     | m < n       =  ocurreEnArbolOrdenado m i
     | otherwise   =  ocurreEnArbolOrdenado m d

Definiciones de distintos tipos de árboles

data Arbol a = Hoja a | Nodo (Arbol a) (Arbol a)  
data Arbol a = Hoja | Nodo (Arbol a) a (Arbol a)
data Arbol a b = Hoja a | Nodo (Arbol a b) b (Arbol a b)  
data Arbol a = Nodo a [Arbol a]  

4 Sistema de decisión de tautologías

Sintaxis de la lógica proposicional

data FProp = Const Bool
           | Var Char
           | Neg FProp
           | Conj FProp FProp
           | Impl FProp FProp
           deriving Show
p1, p2, p3, p4 :: FProp
p1 = Conj (Var 'A') (Neg (Var 'A'))
p2 = Impl (Conj (Var 'A') (Var 'B')) (Var 'A')
p3 = Impl (Var 'A') (Conj (Var 'A') (Var 'B'))
p4 = Impl (Conj (Var 'A') (Impl (Var 'A') (Var 'B'))) 
          (Var 'B')

Semántica de la lógica proposicional

i ¬i
T F
F T
i j i ∧ j i → j
T T T T
T F F F
F T F T
F F F T
A B A → B B → A (A → B) ∨ (B → A)
T T T T T
T F F T T
F T T F T
F F T T T
type Interpretacion = [(Char, Bool)]
valor [('A',False),('B',True)] p3  ==  True
valor [('A',True),('B',False)] p3  ==  False
valor :: Interpretacion -> FProp -> Bool
valor _ (Const b)  = b
valor i (Var x)    = busca x i
valor i (Neg p)    = not (valor i p)
valor i (Conj p q) = valor i p && valor i q
valor i (Impl p q) = valor i p <= valor i q
busca 2 [(1,'a'),(3,'d'),(2,'c')]  ==  'c'  
busca :: Eq c => c -> [(c,v)] -> v
busca c t = head [v | (c',v) <- t, c == c']
variables p3  ==  "AAB"
variables :: FProp -> [Char]
variables (Const _)  = []
variables (Var x)    = [x]
variables (Neg p)    = variables p
variables (Conj p q) = variables p ++ variables q
variables (Impl p q) = variables p ++ variables q
ghci> interpretacionesVar 2
[[False,False],
 [False,True],
 [True,False],
 [True,True]]
interpretacionesVar :: Int -> [[Bool]]
interpretacionesVar 0 = [[]]
interpretacionesVar n = 
    map (False:) bss ++ map (True:) bss
    where bss = interpretacionesVar (n-1)
ghci> interpretaciones p3
[[('A',False),('B',False)],
 [('A',False),('B',True)],
 [('A',True),('B',False)],
 [('A',True),('B',True)]]
interpretaciones :: FProp -> [Interpretacion]
interpretaciones p =  
    [zip vs i | i <- interpretacionesVar (length vs)]
    where vs = nub (variables p)

Decisión de tautología

esTautologia p1  ==  False
esTautologia p2  ==  True
esTautologia p3  ==  False
esTautologia p4  ==  True
esTautologia :: FProp -> Bool
esTautologia p = 
    and [valor i p | i <- interpretaciones p]

5 Máquina abstracta de cálculo aritmético

Evaluación de expresiones aritméticas

data Expr =  Num Int | Suma Expr Expr  
valorEA (Suma (Suma (Num 2) (Num 3)) (Num 4))  ==  9  
valorEA :: Expr -> Int
valorEA (Num n)    = n
valorEA (Suma x y) = valorEA x + valorEA y
  valorEA (Suma (Suma (Num 2) (Num 3)) (Num 4))
= (valorEA (Suma (Num 2) (Num 3))) + (valorEA (Num 4))
= (valorEA (Suma (Num 2) (Num 3))) + 4
= (valorEA (Num 2) + (valorEA (Num 3))) + 4
= (2 + 3) + 4
= 9

Máquina de cálculo aritmético

type PControl = [Op]  
data Op =  METE Expr | SUMA Int  
eval (Suma (Suma (Num 2) (Num 3)) (Num 4)) []  ==  9
eval (Suma (Num 2) (Num 3)) [METE (Num 4)]     ==  9
eval (Num 3) [SUMA 2, METE (Num 4)]            ==  9
eval (Num 4) [SUMA 5]                          ==  9
eval :: Expr -> PControl -> Int
eval (Num n)    p = ejec p n
eval (Suma x y) p = eval x (METE y : p)
ejec [METE (Num 3), METE (Num 4)] 2  ==  9
ejec [SUMA 2, METE (Num 4)]       3  ==  9
ejec [METE (Num 4)]               5  ==  9
ejec [SUMA 5]                     4  ==  9
ejec []                           9  ==  9
ejec :: PControl -> Int -> Int
ejec []           n = n
ejec (METE y : p) n = eval y (SUMA n : p)
ejec (SUMA n : p) m = ejec p (n+m)
evalua (Suma (Suma (Num 2) (Num 3)) (Num 4))  ==  9  
evalua :: Expr -> Int
evalua e = eval e []
eval (Suma (Suma (Num 2) (Num 3)) (Num 4)) []
= eval (Suma (Num 2) (Num 3)) [METE (Num 4)]
= eval (Num 2) [METE (Num 3), METE (Num 4)]
= ejec [METE (Num 3), METE (Num 4)] 2
= eval (Num 3) [SUMA 2, METE (Num 4)]
= ejec [SUMA 2, METE (Num 4)] 3
= ejec [METE (Num 4)] (2+3)
= ejec [METE (Num 4)] 5
= eval (Num 4) [SUMA 5]
= ejec [SUMA 5] 4
= ejec [] (5+4)
= ejec [] 9
= 9

6 Declaraciones de clases y de instancias

Declaraciones de clases

class Eq a where
    (==), (/=) :: a -> a -> Bool

    -- Minimal complete definition: (==) or (/=)
    x == y = not (x/=y)
    x /= y = not (x==y)

Declaraciones de instancias

instance Eq Bool where
    False == False = True
    True  == True  = True
    _     == _     = False

Extensiones de clases

class (Eq a) => Ord a where
    compare                :: a -> a -> Ordering
    (<), (<=), (>=), (>)   :: a -> a -> Bool
    max, min               :: a -> a -> a

    -- Minimal complete definition: (<=) or compare
    -- using compare can be more efficient for complex types
    compare x y | x==y      = EQ
                | x<=y      = LT
                | otherwise = GT

    x <= y                  = compare x y /= GT
    x <  y                  = compare x y == LT
    x >= y                  = compare x y /= LT
    x >  y                  = compare x y == GT

    max x y   | x <= y      = y
              | otherwise   = x
    min x y   | x <= y      = x
              | otherwise   = y

Instancias de clases extendidas

instance Ord Bool where
     False <= _     = True
     True  <= True  = True
     True  <= False = False

Clases derivadas

data Bool = False | True
        deriving (Eq, Ord, Read, Show)
False == False        ==  True
False < True          ==  True
show False            ==  "False"
read "False" :: Bool  ==  False
data Figura = Circulo Float | Rect Float Float
              deriving (Eq, Ord, Show)
ghci> :info Float
...
instance Eq Float   
instance Ord Float  
instance Show Float 
...

7 Bibliografía



Universidad de Sevilla

José A. Alonso Jiménez
Grupo de Lógica Computacional
Dpto. de Ciencias de la Computación e I.A.
Universidad de Sevilla