Ficha 8: Classes de tipos
Voltar
1) Considere o seguinte tipo de dados para representar frações
data Frac = F Integer Integer
a) Defina a função normaliza :: Frac -> Frac
que dada uma fração calcula uma fração equivalente, irredutível, e com o denominador positivo. Por exemplo, normaliza (F (-33) (-51))
deve retornar F 11 17
e normaliza (F 50 (-5))
deve retornar F (-10) 1
. Sugere-se que comece por definir primeiro a função mdc :: Integer -> Integer -> Integer
que calcula o máximo divisor comum entre dois números, baseada na seguinte propriedade (atribuida a Euclides): mdc x y == mdc (x+y) y == mdc x (y+x)
normaliza :: Frac -> Frac
normaliza (F a b)
| b < 0 = normaliza $ F (-a) (-b)
| otherwise =
let d = mdc a b in
F (a `div` d) (b `div` d)
mdc :: Integer -> Integer -> Integer
mdc x 0 = x
mdc 0 y = y
mdc x y = mdc y (x `mod` y)
b) Defina Frac
como instância da classe Eq
.
instance Eq Frac where
(==) :: Frac -> Frac -> Bool
f1 == f2 = a1 == a2 && b1 == b2
where F a1 b1 = normaliza f1
F a2 b2 = normaliza f2
c) Defina Frac
como instância da classe Ord
.
instance Ord Frac where
(<=) :: Frac -> Frac -> Bool
f1 <= f2 = a1 * b2 <= a2 * b1
where F a1 b1 = normaliza f1
F a2 b2 = normaliza f2
d) Defina Frac
como instância da classe Show
, de forma a que cada fracção seja apresentada por (numerador/denominador).
instance Show Frac where
show :: Frac -> String
show f = show a ++ "/" ++ show b
where F a b = normaliza f
e) Defina Frac
como instância da classe Num
. Relembre que a classe Num
tem a seguinte definição:
class Num a where
(+), (*), (-) :: a -> a -> a
negate, abs, signum :: a -> a
fromInteger :: Integer -> a
instance Num Frac where
(+) :: Frac -> Frac -> Frac
(F a b) + (F c d) = normaliza $ F (a * d + b * c) (b * d)
(-) :: Frac -> Frac -> Frac
x - y = x + negate y
(*) :: Frac -> Frac -> Frac
(F a b) * (F c d) = normaliza $ F (a * c) (b * d)
negate :: Frac -> Frac
negate (F a b) = normaliza $ F (-a) b
abs :: Frac -> Frac
abs f = F (abs a) b
where F a b = normaliza f
signum :: Frac -> Frac
signum f = F (signum a) 1
where F a b = normaliza f
fromInteger :: Integer -> Frac
fromInteger x = F x 1
f) Defina uma função que, dada uma fração f
e uma lista de frações l
, seleciona de l
os elementos que são maiores do que o dobro de f
.
maioresQueDobro :: Frac -> [Frac] -> [Frac]
maioresQueDobro = filter . (<) . (2 *)
2) Relembre o tipo definido na Ficha 7 para representar expressões inteiras. Uma possível generalização desse tipo de dados será considerar expressões cujas constantes são de um qualquer tipo numérico (i.e., da classe Num
).
data Exp a = Const a
| Simetrico (Exp a)
| Mais (Exp a) (Exp a)
| Menos (Exp a) (Exp a)
| Mult (Exp a) (Exp a)
a) Declare Exp a
como uma instância de Show
.
instance Show a => Show (Exp a) where
show (Const a) = show a
show (Simetrico a) = "(- " ++ show a ++ ")"
show (Mais a b) = "(" ++ show a ++ " + " ++ show b ++ ")"
show (Menos a b) = "(" ++ show a ++ " - " ++ show b ++ ")"
show (Mult a b) = "(" ++ show a ++ " * " ++ show b ++ ")"
b) Declare Exp a
como uma instância de Eq
.
valueOf :: Num a => Exp a -> a
valueOf (Const a) = a
valueOf (Simetrico a) = negate $ valueOf a
valueOf (Mais a b) = valueOf a + valueOf b
valueOf (Menos a b) = valueOf a - valueOf b
valueOf (Mult a b) = valueOf a * valueOf b
instance (Num a, Eq a) => Eq (Exp a) where
(==) :: (Num a, Eq a) => Exp a -> Exp a -> Bool
a == b = valueOf a == valueOf b
c) Declare Exp a
como instância da classe Num
.
instance (Ord a, Num a) => Num (Exp a) where
(+) :: Num a => Exp a -> Exp a -> Exp a
x + y = Mais x y
(-) :: Num a => Exp a -> Exp a -> Exp a
x - y = Menos x y
(*) :: Num a => Exp a -> Exp a -> Exp a
x * y = Mult x y
negate :: Num a => Exp a -> Exp a
negate (Simetrico a) = a
negate a = Simetrico a
fromInteger :: Num a => Integer -> Exp a
fromInteger x = Const (fromInteger x)
abs :: (Ord a, Num a) => Exp a -> Exp a
abs (Const a) = Const (abs a)
abs (Simetrico a) = abs a
abs op = if valueOf op < 0
then negate op
else op
signum :: Num a => Exp a -> Exp a
signum a | valueOf a < 0 = Const (-1)
| valueOf a == 0 = Const 0
| otherwise = Const 1
3) Relembre o exercício da Ficha 3 sobre contas bancárias, com a seguinte declaração de tipos:
data Movimento = Credito Float | Debito Float
data Data = D Int Int Int
data Extracto = Ext Float [(Data, String, Movimento)]
a) Defina Data
como instância da classe Ord
.
instance Ord Data where
compare :: Data -> Data -> Ordering
compare (D dia1 mes1 ano1) (D dia2 mes2 ano2)
| ano1 > ano2 || ano1 == ano2 && (mes1 > mes2 || mes1 == mes2 && dia1 > dia2) = GT
| ano1 == ano2 && mes1 == mes2 && dia1 == dia2 = EQ
| otherwise = LT
b) Defina Data
como instância da classe Show
.
instance Show Data where
show :: Data -> String
show (D dia mes ano) = intercalate "/" $ map show [ano,mes,dia]
c) Defina a função ordena :: Extracto -> Extracto
, que transforma um extrato de modo a que a lista de movimentos apareça ordenada por ordem crescente de data.
ordena :: Extracto -> Extracto
ordena (Ext n l) = Ext n (sortBy (\(data1,_,_) (data2,_,_) -> compare data1 data2) l)
d) Defina Extracto
como instância da classe Show
de forma a que a apresentação do extracto seja por ordem de data do movimento, com o seguinte aspeto:
Saldo anterior: 300
---------------------------------------
Data Descricao Credito Debito
---------------------------------------
2010/4/5 DEPOSITO 2000
2010/8/10 COMPRA 37,5
2010/9/1 LEV 60
2011/1/7 JUROS 100
2011/1/22 ANUIDADE 8
---------------------------------------
Saldo atual: 2294,5
instance Show Extracto where
show :: Extracto -> String
show ext = "Saldo anterior: " ++ show n ++
"\n---------------------------------------" ++
"\nData Descricao" ++ replicate (desc_max - 9) ' ' ++ "Credito" ++ replicate (cred_max - 7) ' ' ++ "Debito" ++
"\n---------------------------------------\n" ++
unlines (map (\(dat,desc,mov) ->
show dat ++ replicate (data_max - length (show dat)) ' '
++ map toUpper desc ++ replicate (desc_max - length desc) ' '
++ case mov of Credito quant -> show quant ++ replicate (cred_max - length (show quant)) ' '; Debito _ -> replicate cred_max ' '
++ case mov of Debito quant -> show quant; Credito _ -> ""
) movs) ++
"---------------------------------------" ++
"\nSaldo actual: " ++ show (saldo ext)
where (Ext n movs) = ordena ext
data_max = 11
desc_max = max (length "Descricao ") (maximum $ map (\(_,desc,_) -> length desc) movs)
cred_max = max (length "Credito ") (maximum $ map (\(_,_,mov) -> case mov of Credito x -> length (show x); _ -> 0) movs)