Você está na página 1de 34

o Funcional

Programac
a
Caderno de Exerccios
o Frade & Jorge Sousa Pinto
Maria Joa
Departamento de Informatica
Universidade do Minho
2006

1o Ano LCC (2006/07)

Conte
udo
1 Ficha Pr
atica 1
1.1 Valores, Express
oes e Tipos . . .
1.2 Func
oes: Tipos e Definic
ao . . .
1.3 Importac
ao de M
odulos . . . . .
1.4 Introduc
ao `
as Func
oes Recursivas

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

3
3
5
7
8

2 Ficha Pr
atica 2
2.1 Definic
ao (multi-clausal) de Funcoes . . . . .
2.2 Definic
oes Locais . . . . . . . . . . . . . . . .
2.3 Listas e Padr
oes sobre Listas . . . . . . . . .
2.4 Definic
ao de Func
oes Recursivas sobre Listas
2.5 Tipos Sin
onimos . . . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

9
9
11
12
13
14

3 Ficha Pr
atica 3
3.1 Func
oes de Mapeamento, Filtragem, e Folding
3.2 As Func
oes map e filter . . . . . . . . . . .
3.3 A Func
ao foldr . . . . . . . . . . . . . . . .
3.4 Outras Func
oes . . . . . . . . . . . . . . . . .

sobre Listas
. . . . . . . .
. . . . . . . .
. . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

15
15
16
17
18

4 Ficha Pr
atica 4
4.1 Implementac
ao de uma Tabela por uma Lista . . . . . . . . . . . . . . . . . . . . .

4.2 Implementac
ao de uma Tabela por uma Arvore
Binaria de Pesquisa . . . . . . . .

4.3 Arvores de Express


ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21
21
22
22

5 Ficha Pr
atica 5
5.1 Classes, Inst
ancias de Classes e Tipos Qualificados . . . . . . . . . . . . . . . . . .
5.2 Algumas das classes pre-definidas do Haskell . . . . . . . . . . . . . . . . . . . . .

25
25
26

6 Ficha Pr
atica 6
6.1 Input / Output em Haskell . . . . .
6.2 Declarac
ao de Tipos . . . . . . . . .
6.3 Construc
ao de Menus . . . . . . . .
6.4 Manipulac
ao de Ficheiros . . . . . .
6.5 Uma vers
ao melhorada do programa
6.6 Compilac
ao de um programa Haskell

29
29
30
30
32
33
33

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

Ficha Pr
atica 1

Nesta ficha pretende-se trabalhar sobre os seguintes conceitos basicos da linguagem de programacao
funcional Haskell: valores e express
oes; tipos basicos e tipos compostos; operadores pre-definidos;
definicao de func
oes simples; c
alculo de expressoes (passos de reducao) simples; utilizacao de
modulos; utilizac
ao de recursividade na definicao de funcoes.

1.1

Valores, Express
oes e Tipos

Os valores ou constantes s
ao as entidades basicas da linguagem Haskell. As express
oes sao obtidas
combinando-se valores com func
oes, operadores (que sao tambem funcoes) e vari
aveis, que consideraremos na sec
c
ao seguinte. Observe-se que os valores sao casos particulares de expressoes.
Exemplos:
Valores
5
67.9
True
u
abcd

Express
oes
3.8 + 4.6
True && (not False)
((*) 4 ((+) 9 3)) (note os operadores infixos)
8 * 5 + 4 * ((2 + 3) * 8 - 6)
(toLower (toUpper x))

Um conceito muito importante associado a uma expressao e o seu tipo. Os tipos servem para
classificar entidades (de acordo com as suas caractersticas). Em Haskell escrevemos e :: T
para dizer que a express
ao e e do tipo T (ou e tem tipo T).
Exemplos:
5 :: Int
67.9 :: Float
True :: Bool
u :: Char

(3.8 + 4.6) :: Float


(True && (not False)) :: Bool
((*) 4 ((+) 9 3)) :: Int
(8 * 5 + 4 * ((2 + 3) * 8 - 6)) :: Int
(toLower (toUpper x)) :: Char

Tipos B
asicos
O Haskell oferece os seguintes tipos b
asicos:
Bool - Boleanos: True, False
Char - Caracteres: a, x, R, 7, \n, ...
Int - Inteiros de tamanho limitado: 1, -4, 23467, ...
Integer - Inteiros de tamanho ilimitado: -6, 36, 45763456623443249, ...
Float - N
umeros de vrgula flutuante: 3.5, -45.78, ...
Double - N
umeros de vrgula flutuante de dupla precisao: -45.63, 3.678, 51.2E7, ...
() - Unit: ()

Tipos Compostos
Produtos Cartesianos
(a1, a2, ..., an) :: (T1, T2, ..., Tn),
sendo a1 to tipo T1, a2 do tipo T2, ... an do tipo Tn.

Exemplos:
(3, d) :: (Int,Char)
(True, 5.7, 3) :: (Bool, Float, Int)
(k, (6, 2), False) :: (Char, (Int, Int), Bool)
Listas
[a1, a2, ..., an]::[T] todos os elementos, ai, da lista, sao do tipo T.
Exemplos:
[3, 4, 3, 7, 8, 2, 5] :: [Int]
[r, c, e, 4, d] :: [Char] (nota: [r, c, e, 4, d]=rce4d)
[(a,5), (d, 3), (h, 9)] :: [(Char,Int)]
[[5,6], [3], [9,2,6], [], [1,4]] :: [[Int]]

C
alculo do Valor de uma Express
ao
Um interpretador de Haskell (no nosso caso o ghci) usa definicoes de funcoes e operadores como
regras de c
alculo, para calcular o valor de uma expressao. Por exemplo, a expressao 8 * 5 + 4 *
((2 + 3) * 8 - 6) e calculada pelos seguintes passos:
8 * 5 + 4 * ((2 + 3) * 8 - 6) 40 + 4 * (5 * 8 - 6)
40 + 4 * (40 - 6)
40 + 4 * 34
40 + 136
176
Tarefa 1
No ghci faca:
>
>
>
>
>
>
>
>
>
>

:set +t
fst (4, a)
snd (4, a)
fst (5.6, 3)
:i fst
:t fst
:i tail
:t tail
tail [6,7,3,9]
tail sdferta

Observe o mecanismo de inferencia de tipos do Haskell e o polimorfismo das func


oes fst e tail.
Tarefa 2
Infira o tipo, se existir, de cada uma das seguintes express
oes, e avalie-a:
[True, (5>4), (not (5==6)), (True || (False && True))]
((tail abcdef),(head abcdef))
[(tail abcdef),(head abcdef)]
[4,5,6]++[3,5,8]
(tail [6,7])
concat [asdf, bbb, tyuui, cccc]

1.2

Func
oes: Tipos e Defini
c
ao

Em Haskell as func
oes s
ao objectos com o mesmo estatuto que os valores de tipos basicos e
compostos. Quer isto dizer que tem igualmente um tipo.
Fun
c
oes
f :: T1 -> T2 func
oes que recebem valores do tipo T1 e devolvem valores do tipo T2.
(f a) :: T2 aplicac
ao da func
ao f ao argumento a do tipo T1.
Exemplos:
toLower :: Char -> Char
not :: Bool -> Bool
ord :: Char -> Int
chr :: Int -> Char
fst :: (a, b) -> a
tail :: [a] -> [a]
Ha funcoes `
as quais e possvel associar mais do que um tipo (funcoes polimorficas). O Haskell
recorre a vari
aveis de tipo (a, b, c, ...) para expressar o tipo de tais funcoes. Uma variavel de tipo
representa um tipo qualquer. Quando as funcoes sao usadas, as variaveis de tipo sao substitudas
pelos tipos concretos adequados.
Um princpio fundamental de qualquer linguagem do programacao com tipos e do compatibilidade do tipo de uma func
ao com os tipos dos seus argumentos. A verificacao desta condicao antes
da compilac
ao dos programas protege-os da ocorrencia de muitos erros durante a sua execucao.
Para ilustrar o comportamento do interpretador nesta situacao, tente avaliar a seguinte expressao:
> tail 45

Fun
c
oes Pr
e-definidas
O Haskell oferece um conjunto de funcoes pre-definidas (cf. o modulo Prelude). Alguns exemplos
foram introduzidos anteriormente:
operadores l
ogicos &&, ||, not ;
operadores relacionais

>, <=, == ;

operadores sobre produtos cartesianos


operadores sobre listas

fst, snd ;

head, tail, length, reverse, concat, ++ .

Fun
c
oes Definidas pelo Programador
Mas podemos tambem definir novas funcoes. Uma funcao e definida por uma equacao que relaciona
os seus argumentos com o resultado pretendido:
<nomefun
c~
ao> <arg1>...<argn> = <express~
ao>
Por exemplo:
ex a = 50 * a
funcao1 x y = x + (70*y)

Depois de definidas estas func


oes poderemos utiliza-las para construir novas expressoes, que serao
avaliadas de acordo com as definic
oes das funcoes. Por exemplo:
funcao1 (ex 10) 1

(ex 10) + (70*1)


(50*10) + (70*1)
500 + (70*1)
500 + 70
570

O tipo de cada func


ao e inferido automaticamente pelo interpretador; no entanto e considerado
boa pratica incluir explicitamente na definicao de uma funcao o seu tipo.
Tomemos um segundo exemplo: uma funcao que recebe um par de inteiros e da como resultado
o maior deles. Esta func
ao, a que chamaremos maior, podera ser definida como se segue:
maior :: (Int, Int) -> Int
maior (x,y) = if (x > y) then x else y
Se quisermos agora definir uma func
ao que calcule o maior de tres inteiros, poderemos usar a
funcao anterior nessa definic
ao:
maiorde3 :: Int -> Int -> Int -> Int
maiorde3 x y z = (maior ((maior (x,y)), z))

M
odulos de C
odigo Haskell
As definic
oes de func
oes n
ao podem ser introduzidas directamente no interpretador de Haskell,
devendo antes ser escritas num ficheiro a que chamaremos um m
odulo. Um m
odulo e em geral um
ficheiro contendo um conjunto de definicoes (declaracoes de tipos, de funcoes, de classes, ...) que
serao lidas pelo interpretador, e depois utilizadas pelo mesmo.
Um m
odulo Haskell e armazenado num ficheiro com extensao .hs, <nome>.hs, em que <nome>
representa o nome do m
odulo, que tera que ser declarado na primeira linha do ficheiro. Por
exemplo, o ficheiro Teste.hs dever
a comecar com a declaracao seguinte:
module Teste where
....
Um exemplo de um m
odulo contendo duas das funcoes acima definidas sera:
module Teste where
funcao1 x y = x + (70*y)
ex a = 50 * a
Tarefa 3
1. Crie um ficheiro com o m
odulo acima apresentado.
2. No ghci carregue este m
odulo, escrevendo
> :l Teste.hs
3. Use o interpretador ghci para verificar qual o tipo das func
oes definidas no m
odulo.
4. Avalie depois a express
ao
funcao1 (ex 10) 1.

Tarefa 4
Defina e teste as seguintes func
oes (sugest
ao: crie um m
odulo com o nome Ficha1 para incluir as
definic
oes que efectuar nesta aula).
1. Defina uma func
ao que receba dois pares de inteiros e retorne um par de inteiros, sendo o
primeiro elemento do par resultado a soma dos primeiros elementos dos pares de entrada, e
o segundo elemento do par, o produto dos segundos elementos dos pares de entrada.
2. Escreva uma func
ao que, dados tres n
umeros inteiros, retorne um par contendo no primeiro
elemento o maior dos n
umeros, e no segundo elemento o segundo maior dos n
umeros.
3. Escreva uma func
ao que receba um triplo de n
umeros inteiros e retorne um triplo em que os
mesmos n
umeros est
ao ordenados por ordem decrescente.
4. Os lados de qualquer tri
angulo respeitam a seguinte restric
ao: a soma dos comprimentos de
quaisquer dois lados, e superior ao comprimento do terceiro lado. Escreva uma func
ao que
receba o comprimento de tres segmentos de recta e retorne um valor booleano indicando se
satisfazem esta restric
ao.
5. Escreva uma func
ao abrev que receba uma string contendo nome de uma pessoa e retorne
uma string com o primeiro nome e apelido1
(e.g. (abrev Joao Carlos Martins Sarmento)=Joao Sarmento)
As func
oes, pre-definidas, words e unwords poder
ao ser-lhe uteis
words :: String -> [String], d
a como resultado a lista das palavras (strings) de
um texto (uma string)
unwords :: [String] -> String, constroi um texto (uma string) a partir de uma
lista de palavras (strings).

1.3

Importac
ao de M
odulos

Um programa em Haskell e formado por um conjunto de modulos. As definicoes de cada modulo


podem ser utilizadas internamente, ou exportadas para ser utilizadas noutros modulos.
Para utilizar definic
oes contidas num outro modulo e necessario importa-lo explicitamente. Este
tipo de ligac
ao entre m
odulos estabelece-se utilizando uma declaracao import. Como exemplo,
vejamos como podemos ter acesso `
as funcoes de manipulacao de caracteres e strings (listas de
caracteres) disponveis no m
odulo Char.
module Conv1 where
import Char
con = toLower A
fun x = toUpper x
Uma excepc
ao a esta regra e o m
odulo Prelude, que constitui a base da linguagem Haskell,
e cujas definic
oes est
ao sempre disponveis em todos os outros modulos, sem que seja necessario
importa-lo.
Tarefa 5
1. Crie um ficheiro com o m
odulo acima apresentado. Use o interpretador ghci para experimentar a func
ao fun e ver o valor da constante con.
2. Crie um ficheiro Exemp.hs com o m
odulo seguinte:
1 Considere

que o apelido s
o tem um nome.

module Exemp where


import Conv2
import Char
conv x = if (isAlpha x) then (upperandlower x)
else []
e outro ficheiro com o m
odulo Conv2:
module Conv2 where
import Char
upperandlower c = [(toLower c), (toUpper c)]
Carregue o ficheiro Exemp.hs no ghci e experimente as func
oes conv e upperandlower.
Verifique qual o tipo das func
oes.
Tarefa 6
Consulte as definic
oes oferecidas pelo m
odulo Char, escrevendo
> :b Char

1.4

Introduc
ao `
as Func
oes Recursivas

Considere agora a seguinte definic


ao matematica da funcao factorial para n
umeros inteiros nao
negativos:
0! = 1
n! = n*(n-1)*(n-2)*...*1
Notando que n! = n*(n-1)!, uma possivel definicao em Haskell da funcao factorial, sera:
fact :: Int -> Int
fact n = if (n==0) then 1
else n * fact (n-1)
Repare que esta func
ao e recursiva, i.e. ela aparece na propria expressao que a define. Diz-se
tambem que a func
ao se invoca a si pr
opria. O calculo da funcao termina porque se atinge sempre
o caso de paragem (n=0).
Tarefa 7
1. Defina uma func
ao que calcule o resultado da exponenciac
ao inteira xy sem recorrer a
func
oes pre-definidas.
2. Defina uma func
ao que recebe uma lista constr
oi o par com o primeiro e o u
ltimo elemento
da lista.
3. Defina uma func
ao que dada uma lista d
a o par com essa lista e com o seu comprimento.
4. Defina uma func
ao que dada uma lista de n
umeros calcula a sua media.

Ficha Pr
atica 2

Nesta ficha pretende-se trabalhar sobre os seguintes conceitos basicos da linguagem de programacao
funcional Haskell: noc
ao de padr
ao e de concordancia de padroes; definicoes multi-clausais de
funcoes e a sua relac
ao com a reduc
ao (calculo) de expressoes; definicao de funcoes com guardas,
definicoes locais. Pretende-se ainda trabalhar na definicao de funcoes recursivas sobre listas, e na
definicao de tipos sin
onimos.

2.1

Definic
ao (multi-clausal) de Fun
c
oes

A definic
ao de func
oes pode ser feita por um conjunto de equacoes da forma:
nome arg1 arg2 ... argn = expressao
em que cada argumento da func
ao tem que ser um padr
ao. Um padrao pode ser uma variavel,
uma constante, ou um esquema de um valor atomico (isto e, o resultado de aplicar construtores
basicos dos valores a outros padr
oes). Alem disso, estes padroes nao podem ter variaveis repetidas
(padr
oes lineares).
Exemplo:

5 e um padr
ao do tipo Int;
[x,A,y] e um padr
ao do tipo [Char];
(x,8,(True,b)) e um padrao do tipo (a,Int,(Bool,b)).

Mas, [x,a,1], (2,x,(z,x)) e (4*5,y) nao podem ser padroes de nenhum tipo. Porque
?
Quando se define uma func
ao podemos incluir informacao sobre o seu tipo. No entanto, essa
informacao n
ao e obrigat
oria.
O tipo de cada func
ao e inferido automaticamente pelo interpretador. Essa inferencia tem
possivel
por base o princpio de que ambos os lados da equacao tem que ser do mesmo tipo. E
declararmos para uma func
ao um tipo mais especifico do que o tipo inferido automaticamente.
Exemplo:

seg :: (Bool,Int) -> Int


seg (x,y) = y
Se n
ao indicarmos o tipo seg::(Bool,Int)->Int qual sera o tipo de seg ?

Podemos definir uma func


ao recorrendo a varias equacoes, mas todas as equacoes tem que ser
bem tipadas e de tipos coincidentes.
Exemplo:

f
f
f
f

:: (Int,Char,Int) -> Int


(y,a,x) = y+x
(z,b,x) = z*x
(x,y,z) = x

Cada equac
ao e usada como regra de reduc
ao (calculo). Quando uma funcao e aplicada a um
argumento, a equac
ao que e selecionada como regra de reducao e a 1a equacao (a contar de cima)
cujo padr
ao que tem como argumento concorda com o argumento actual (pattern matching).
Note que podem existir v
arias equacoes com padroes que concordam com o argumento actual.
Por isso, a ordem das equac
oes e importante, pois define uma prioridade na escolha da regra de
reducao.
Tarefa 1
Indique, justificando, o valor das seguintes express
oes:
i) f (3,a,5)
ii) f (9,B,0)
iii) f (5,b,4)
O que acontece se alterar a ordem das equac
oes que definem f ?
9

Tarefa 2
Considere a seguinte func
ao:
opp :: (Int,(Int,Int)) -> Int
opp z = if ((fst z) == 1)
then (fst (snd z)) + (snd (snd z))
else if ((fst z) == 2)
then (fst (snd z)) - (snd (snd z))
else 0
Defina uma outra vers
ao func
ao opp que tire proveito do mecanismo de pattern matching. Qual
das vers
oes lhe parece mais legvel ?
Em Haskell e possvel definir func
oes com alternativas usando guardas. Uma guarda e uma expressao booleana. Se o seu valor for True a equacao correspondente sera usada na reducao (senao
o interpretador tenta utilizar a equacao seguinte).
Exemplo: As func
oes sig1, sig2 e sig3 sao equivalentes. Note que sig2 e sig3 usam guardas.
otherwise e equivalente a True.
sig1 x y = if x > y then 1
else if x < y then -1
else 0
sig2 x y | x > y = 1
| x < y = -1
| x == y = 0
sig3 x y
| x > y
= 1
| x < y
= -1
| otherwise = 0
Tarefa 3
1. Defina novas vers
oes da func
ao opp usando definic
oes com guardas.
2. Relembre a func
ao factorial definida na u
ltima ficha. Podemos definir a mesma func
ao
declarando as duas cl
ausulas que se seguem:
fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n-1)
Esta definic
ao de fact comporta-se bem sobre n
umeros naturais, mas se aplicarmos fact
a um n
umero negativo (o que matem
aticamente n
ao faz sentido) a func
ao n
ao termina
(verifique). Use uma guarda na definic
ao de fact para evitar essa situac
ao.
O Haskell aceita como padr
oes sobre n
umeros naturais, expressoes da forma: (variavel + n
umero
natural). Estes padr
ao s
o concorda com n
umeros nao inferiores ao n
umero natural que esta no
padrao. Por exemplo, o padr
ao (x+3) concorda com qualquer inteiro maior ou igual a 3, mas nao
concorda com 1 ou 2. Note ainda que expressoes como, por exemplo, (n*5), (x-4) ou (2+n), nao
sao padroes. (Porque ?)
Exemplo: Podemos escrever uma outra versao da funcao factorial equivalente `a funcao que
acabou de definir, do seguinte modo:
10

fact 0 = 1
fact (n+1) = (n+1) * fact n
Note como esta func
ao assim declarada deixa de estar definida para n
umeros negativos.
Tarefa 4
Considere a definic
ao matem
atica dos n
umeros de Fibonacci:
f ib(0) = 0
f ib(1) = 1
f ib(n) = f ib(n 2) + f ib(n 1)

se n 2

Defina em Haskell a func


ao de Fibonacci.

2.2

Definic
oes Locais

Todas as definic
oes feitas ate aqui podem ser vistas como globais, uma vez que elas sao visveis no
modulo do programa aonde est
ao. Mas, muitas vezes e u
til reduzir o ambito de uma declaracao.
Em Haskell h
a duas formas de fazer definic
oes locais: utilizando expressoes let...in ou atraves
de clausulas where junto da definic
ao equacional de funcoes.
Exemplo: As func
oes dividir1, dividir2 e dividir3 sao equivalentes. As declaracoes de q
e r s
ao apenas visveis na expressao que esta a seguir a in. As declaracoes de quociente
e resto s
ao apenas visveis no lado direito da equacao que antecede where. (Teste estas
afirmac
oes.)
dividir1 x y = (div x y, mod x y)
dividir2 x y = let q = div x y
r = x mod y
in (q,r)
dividir3 x y = (quociente,resto)
where quociente = x div y
resto = mod x y
Tambem e possvel fazer declarac
oes locais de funcoes.
Tarefa 5
e uma vari
avel an
onima nova (
util para argu-

1. Analise e teste a func


ao exemplo. Nota:
mentos que n
ao s
ao utilizados).
exemplo y = let k = 100
g (1,w,z) =
g (2,w,z) =
g (_,_,_) =
in ((f y) + (f
where c = 10
(a,b) = (3*c, f 2)
f x = x + 7*c

w+z
w-z
k
a) + (f b) , g (y,k,c))

2. A seguinte func
ao calcula as raizes reais de um poln
omio a x2 + b x + c. Escreva outras
vers
oes desta func
ao (por exemplo: com let...in, sem guardas, ...).

11

raizes :: (Double,Double,Double) -> (Double,Double)


raizes (a,b,c) = (r1,r2)
where r1 = (-b + r) / (2*a)
r2 = (-b - r) / (2*a)
d = b^2 - 4*a*c
r | d >= 0 = sqrt d
| d < 0 = error raizes imaginarias
Nota: error e uma func
ao pre-definida que permite indicar a mensagem de erro devolvida
pelo interpretador. (Qual ser
a o seu tipo ?)

2.3

Listas e Padr
oes sobre Listas

Como ja vimos o Haskell tem pre-definido o tipo [a] que e o tipo das listas cujos elementos sao
todos do tipo a. Relembre que a e uma variavel de tipo que representa um dado tipo (que ainda
e uma inc
ognita).
Na realidade, as listas s
ao construidas `a custa de dois construtores primitivos:
a lista vazia, [] ::

[a]

o construtor (:) :: a -> [a] -> [a], que e um operador infixo que dado um elemento
x de tipo a e uma lista l de tipo [a], constroi uma nova lista, x:l, com x na 1a posicao
seguida de l.
Exemplo: [1,2,3] e uma abreviatura de 1:(2:(3:[])), que e igual a 1:2:3:[] porque (:) e
associativa `
a direita. Portanto, as expressoes: [1,2,3], 1:[2,3], 1:2:[3] e 1:2:3:[] sao
todas equivalentes. (Teste esta afirmacao no ghci.)
Os padr
oes do tipo lista s
ao expressoes envolvendo apenas os seus construtores [] e (:), ou a
representac
ao abreviada de listas. Padroes com o construtor (:) terao que estar envolvidos por
parentesis.
Exemplo:

Uma func
ao que testa se uma lista e vazia pode ser definida por:

vazia [] = True
vazia (x:xs) = False
Tarefa 6
1. Defina uma vers
ao alternativa para a func
ao vazia.
2. A func
ao que soma os elementos de uma lista ate `
a 3a posic
ao pode ser definida da seguinte
forma:
soma3
soma3
soma3
soma3
soma3

:: [Integer] -> Integer


[] = 0
(x:y:z:t) = x+y+z
(x:y:t) = x+y
(x:t) = x

Em soma3 a ordem das equac


oes e importante? Porque? Ser
a que obtemos a mesma func
ao
se alterarmos a ordem das equac
oes?
Defina uma func
ao equivalente a esta usando apenas as func
oes pre-definidas take e sum.

12

Tarefa 7
1. Defina a func
ao transf::[a]->[a] que faz a seguinte transformac
ao: recebe uma lista e,
caso essa lista tenha pelo menos 4 elementos, troca o 1o com o 2o elemento, e o u
ltimo com o
pen
ultimo elemento da lista. Caso contr
ario, devolve a mesma lista. Por exemplo: transf
[1,2,3,4,5,6,7] [2,1,3,4,5,7,6].
( Sugest
ao: as func
oes pre-definidas length ou reverse poder
ao ser-lhe uteis.)
2. Defina uma func
ao somaPares24::[(Int,Int)]->(Int,Int) que recebe uma lista de pares
de inteiros e calcula a soma do 2o com o 4o par da lista.
3. Estas func
oes que definiu s
ao totais ou parciais ?

2.4

Definic
ao de Func
oes Recursivas sobre Listas

As listas s
ao definidas de uma forma recursiva como:
1. [] (a lista vazia) e uma lista;
2. Se x :: a (i.e., x e do tipo a) e t :: [a] (i.e., t e uma lista com elementos do tipo a)
ent
ao (x:t) :: [a] (i.e., x:t e uma lista com elementos do tipo a).
Esta definic
ao conduz a uma estrategia para definir funcoes sobre listas.
Exemplo:

A func
ao que calcula a soma dos elementos de uma lista pode ser definida como:

soma [] = 0
soma (h:t) = h + (soma t)
Exemplo:

A func
ao que calcula o comprimento de uma lista esta pre-definida no Prelude por:

length :: [a] -> Int


length [] = 0
length (_:t) = 1 + (length t)
Exemplo: Uma func
ao que recebe uma lista de pontos no plano cartesiano e calcula a distancia
de cada ponto `
a origem, pode definida por:
distancias :: [(Float,Float)] -> [Float]
distancias [] = []
distancias ((x,y):xys) = (sqrt (x^2 + y^2)) : (distancias xys)
Tarefa 8
Use a estrategia sugerida acima para definir as seguintes func
oes.
1. A func
ao que calcula o produto de todos os elementos de uma lista de n
umeros.
2. A func
ao que, dada uma lista e um elemento, o coloca no fim da lista.
3. A func
ao que, dadas duas listas as concatena, i.e., calcula uma lista com os elementos da
primeira lista seguidos dos da segunda lista. (Sugest
ao: analise o que acontece para ambos
os casos da primeira lista.)
Para definirmos uma func
ao que calcula a media dos elementos de uma lista de n
umeros podemos calcular a soma dos seus elementos e o comprimento da lista retornando depois o quociente
entre estes dois valores. (Nota: a funcao fromIntegral, pre-definida, e aqui usada para fazer a
conversao de um valor inteiro para real.)
media1 l = let s = sum l
c = length l
in s / (fromIntegral c)
Esta soluc
ao corresponde a percorrer a lista 2 vezes.
13

Tarefa 9
1. Defina uma func
ao que, dada uma lista, calcula um par contendo o comprimento da lista e
a soma dos seus elementos, percorrendo a lista uma u
nica vez.
2. Usando a func
ao anterior defina uma func
ao que calcula a media dos elementos de uma
lista.
Ha no entanto func
oes em que e mais difcil evitar estas m
ultiplas travessias da lista.
Tarefa 10
1. Defina uma func
ao que, dada uma lista, a divida em duas (retornando um par de listas)
com o mesmo n
umero de elementos (isto, e claro, se a lista original tiver um n
umero par de
elementos; no outro caso uma das listas ter
a mais um elemento).
2. Defina uma func
ao que, dada uma lista e um valor, retorne um par de listas em que a
primeira contem todos os elementos da lista inferiores a esse valor e a segunda lista contem
todos os outros elementos.
3. Defina uma func
ao que, dada uma lista de n
umeros, retorne a lista com os elementos que
s
ao superiores `
a media.

2.5

Tipos Sin
onimos

Existe em Haskell a possibilidade de definir abreviaturas para tipos. Por exemplo, o tipo String
esta pre-definido como uma abreviatura para o tipo [Char], atraves da declaracao
type String = [Char]
Exemplo: Considere que queremos definir funcoes de manipulacao de uma lista telefonica. Para
isso resolvemos que a informac
ao de cada entrada na lista telefonica contera o nome, no de
telefone e endereco de e-mail. Podemos entao fazer as seguintes definicoes:
type Entrada = (String, String, String)
type LTelef = [Entrada]
A func
ao que calcula os enderecos de email conhecidos pode ser definida como
emails :: LTelef -> [String]
emails [] = []
emails ((_,_,em):t) = em : (emails t)
Note que, uma vez que o tipo String e por sua vez uma abreviatura de [Char], o tipo da
func
ao emails acima e equivalente a
emails ::

([([Char],[Char],[Char])] -> [[Char]]

Tarefa 11
1. Construa um m
odulo com as definic
oes apresentadas acima e verifique (usando o comando
:t) o tipo da func
ao emails.
2. Defina uma func
ao que, dada uma lista telef
onica, produza a lista dos enderecos de email
das entradas cujos n
umeros de telefone s
ao da rede fixa (prefixo 2). N
ao se esqueca de
explicitar o tipo desta func
ao.
3. Defina uma func
ao que dada uma lista telef
onica e um nome, devolva o par com o no de
telefone e o endereco de e-mail associado a esse nome, na lista telef
onica.

14

Ficha Pr
atica 3

Nesta aula pretende-se por um lado trabalhar sobre padroes de funcoes recursivas sobre listas
(mapeamento, filtragem, e folding), e por outro utilizar funcoes de ordem superior (map, filter
e foldr) para definir de forma mais expedita essas mesmas funcoes. Finalmente, trabalhar-se-a
sobre a definic
ao de outras func
oes de ordem superior.

3.1

Func
oes de Mapeamento, Filtragem, e Folding sobre Listas

Certas func
oes recursivas sobre listas seguem padroes rgidos, o que permite classifica-las nas
seguintes tres categorias:
1. Mapeamento: aplicam a todos os elementos da lista argumento uma mesma funcao, obtendose como resultado uma lista com a mesma dimensao. Por exemplo:
dobros :: [Int] -> [Int]
dobros []
= []
dobros (x:xs) = (2*x):(dobros xs)
impares :: [Int] -> [Bool]
impares []
= []
impares (x:xs) = (odd x):(impares xs)
2. Filtragem: calculam como resultado uma sub-sequencia da lista argumento, contendo (pela
mesma ordem) apenas os elementos que satisfazem um determinado criterio. Por exemplo:
umaouduasletras :: [String] -> [String]
umaouduasletras [] = []
umaouduasletras (x:xs)
| (length x)<= 2 = x:(umaouduasletras xs)
| otherwise
= umaouduasletras xs
filtra_impares :: [Int] -> [Bool]
filtra_impares [] = []
filtra_impares (x:xs)
| odd x
= x:(filtra_impares xs)
| otherwise = filtra_impares xs
3. Folding: Combinam atraves de uma operacao binaria todos os elementos da lista. Mais
exactamente, iteram uma operacao binaria sobre uma lista, o que corresponde `as bem conhecidas operac
oes matem
aticas de somatorio ou produtorio sobre conjuntos. Para a
lista vazia resulta um qualquer valor constante (tipicamente o elemento neutro da operacao
bin
aria, mas pode ser outro qualquer valor). Exemplos:
somaLista :: [Int] -> Int
somaLista []
= 0
somaLista (x:xs) = x+(somaLista xs)
multLista :: [Int] -> Int
multLista []
= 1
multLista (x:xs) = x*(multLista xs)

15

Tarefa 1
Defina as seguintes tres novas func
oes, e diga se correspondem a algum dos padr
oes acima referidos.
1. Func
ao paresord que recebe uma lista de pares de n
umeros e devolve apenas os pares em
que a primeira componente e inferior `
a segunda.
2. Funcc
ao myconcat que recebe uma lista de strings e as junta (concatena) numa u
nica string.
3. Func
ao maximos que recebe uma lista de pares de n
umeros (de tipo float) e calcula uma
lista contendo em cada posic
ao o maior elemento de cada par.

3.2

As Func
oes map e filter

Para possibilitar a definic


ao f
acil de funcoes que seguem os padroes anteriormente mencionados, e
possvel captar-se esses padr
oes em funcoes recursivas mais abstractas. Para o caso do mapeamento
e da filtragem, temos as duas seguintes funcoes (pre-definidas):
map :: (a -> b) -> [a] -> [b]
map f []
= []
map f (x:xs) = (f x) : (map f xs)
filter :: (a ->
filter p []
filter p (x:xs)
| p x
| otherwise

Bool) -> [a] -> [a]


= []
= x:(filter p xs)
= filter p xs

Observe-se que estas func


oes, ditas de ordem superior, recebem como argumento uma outra func
ao,
que especifica, no caso de map, qual a operacao a aplicar a cada elemento da lista, e no caso de
filter, qual o criterio de filtragem (dado por uma funcao de teste, ou predicado). Assim,
(map f l) aplica a func
ao f a todos os elementos da lista l. Observe a concordancia de
tipos entre os elementos da lista l e o dominio da funcao f.
(filter p l) seleciona/filtra da lista l os elementos que satisfazem o predicado p. Observe
a concord
ancia de tipos entre os elementos da lista l e o domnio do predicado p.
A utilizac
ao destas func
oes permite a definicao de funcoes implicitamente recursivas. Por
exemplo, uma nova vers
ao da func
ao dobros pode ser definida como:
dobros l = map (2*) l
ou de forma ainda mais simples, dobros = map (2*).
Tarefa 2
Avalie cada uma das seguintes express
oes
1. map odd [1,2,3,4,5]
2. filter odd [1,2,3,4,5]
3. map (\x-> div x 3) [5,6,23,3]
4. filter (\y-> (mod y 3 == 0)) [5,6,23,3]
5. filter (7<) [1,3..15]

16

6. map (7:) [[2,3],[1,5,3]]


7. map (:[]) [1..5]
8. map succ (filter odd [1..20])
9. filter odd (map succ [1..20])
Tarefa 3
Defina novas vers
oes de todas as func
oes de mapeamento e filtragem definidas na secc
ao anterior,
utilizando para isso as func
oes de ordem superior acima referidas.
Tarefa 4
Considere a func
ao seguinte
indicativo :: String -> [String] -> [String]
indicativo ind telefs = filter (concorda ind) telefs
where concorda :: String -> String -> Bool
concorda [] _ = True
concorda (x:xs) (y:ys) = (x==y) && (concorda xs ys)
concorda (x:xs) [] = False
que recebe uma lista de algarismos com um indicativo, uma lista de listas de algarismos representando n
umeros de telefone, e seleciona os n
umeros que comecam com o indicativo dado. Por
exemplo:
indicativo "253" ["253116787","213448023","253119905"]
devolve ["253116787","253119905"].
Redefina esta func
ao com recursividade explcita, isto e, evitando a utilizac
ao de filter.
Tarefa 5
Considere a func
ao seguinte
abrev :: [String] -> [String]
abrev lnoms = map conv lnoms
where conv :: String -> String
conv nom = let ns = (words nom)
in if (length ns) > 1
then (head (head ns)):(". " ++ (last ns))
else nom
que converte uma lista de nomes numa lista de abreviaturas desses nomes, da seguinte forma:
[Jo
ao Carlos Mendes, Ana Carla Oliveira] em [J. Mendes, A. Oliveira].
Defina agora esta func
ao com recursividade explcita, isto e, evitando a utilizac
ao de map.

3.3

A Func
ao foldr

A funcao foldr, tal como map e filter, permite escrever de forma expedita, sem recursividade
explcita, um grande conjunto de func
oes (incluindo as proprias funcoes map e filter).
O funcionamento desta func
ao pode ser facilmente compreendido se se considerar que os constructores (:) e [] s
ao simplesmente substitudos pelos dois parametros de foldr. Por exemplo,
recordando que [1,2,3] == 1:(2:(3:[])), tem-se que

17

foldr (+) 0 [1,2,3] => 1+(2+(3+0))


foldr (*) 1 [1,2,3] => 1*(2*(3*1))
Isto permite definir:
somaLista l = foldr (+) 0 l
multLista l = foldr (*) 1 l
Tarefa 6
Investigue o tipo e funcionamento de cada uma das seguintes func
oes do Haskell:
concat
and
or
Escreva definic
oes destas func
oes usando foldr.
Tarefa 7
Considere a seguinte definic
ao de uma func
ao que separa uma lista em duas partes de comprimento
identico:
separa [] = ([],[])
separa (h:t) = (h:r,l)
where (l,r) = separa t
Escreva uma nova definic
ao desta func
ao recorrendo a foldr.

3.4

Outras Func
oes

Na resoluc
ao das tarefas 8 e 9 utilize, sempre que lhe pareca natural, as funcoes map, filter, e
foldr.
Tarefa 8
Pretende-se guardar a informac
ao sobre os resultados dos jogos de uma jornada de um campeonato
de futebol na seguinte estrutura de dados:
type
type
type
type

Jornada = [Jogo]
Jogo = ((Equipa,Golos),(Equipa,Golos))
Equipa = String
Golos = Int

Defina as seguintes func


oes:
1.

igualj :: Jornada -> Bool


que verifica se nenhuma equipa joga com ela pr
opria.

2.

semrepet :: Jornada -> Bool


que verifica se nenhuma equipa joga mais do que um jogo.

3.

equipas :: Jornada -> [Equipa]


que d
a a lista das equipas que participam na jornada.

4.

empates :: Jornada -> [(Equipa,Equipa)]


que d
a a listas dos pares de equipas que empataram na jornada.
18

5.

pontos :: Jornada -> [(Equipa,Int)]


que calcula os pontos que cada equipa obteve na jornada (venceu - 3 pontos; perdeu - 0
pontos; empatou - 1 ponto)

Tarefa 9
Uma forma de representar polin
omios de uma vari
avel e usar listas de pares (coeficiente, expoente)
type Pol = [(Float,Int)]
Note que o polin
omio pode n
ao estar simplificado. Por exemplo,
[(3.4,3), (2.0,4), (1.5,3), (7.1,5)]

:: Pol

representa o polin
omio 3.4 x3 + 2 x4 + 1.5 x3 + 7.1 x5 .
1. Defina uma func
ao para ordenar um polin
omio por ordem crescente de grau.
2. Defina uma func
ao para normalizar um polin
omio.
3. Defina uma func
ao para somar dois polin
omios nesta representac
ao.
4. Defina a func
ao de c
alculo do valor de um polin
omio num ponto.
5. Defina uma func
ao que dado um polin
omio, calcule o seu grau.
6. Defina uma func
ao que calcule a derivada de um polin
omio.
7. Defina uma func
ao que calcule o produto de dois polin
omios.
8. Ser
a que podemos ter nesta representac
ao de polin
omios, mon
omios com expoente negativo
? As func
oes que definiu contemplam estes casos ?
Tarefa 10
Considere as duas seguintes func
oes:
merge :: (Ord a) => [a] -> [a] -> [a]
insert :: (Ord a) => a -> [a] -> [a]
A primeira efectua a fus
ao de duas listas ordenadas de forma crescente; a segunda insere um
elemento numa lista ordenada de forma crescente:
merge [1,4] [2,3] => [1,2,3,4]
insert "bb" ["aa","cc"] => ["aa","bb","cc"]
Uma definic
ao possvel de insert e
insert x [] = [x]
insert x (h:t)
| (x<=h) = x:h:t
| otherwise = h:(insert x t)
1. Escreva a func
ao merge utilizando foldr e insert.
2. Relembre o algoritmo de ordenac
ao insertion sort, implementado em Haskell por uma func
ao:
isort :: (Ord a) => [a] -> [a]
Reescreva esta func
ao utilizando foldr.

19

20

Ficha Pr
atica 4

Nesta ficha pretende-se trabalhar com tipos de dados indutivos. Em particular utilizar-se-a o tipo
Maybe a, e tipos definidos pelo utilizador com mais do que um construtor. Finalmente trabalharse-a com tipos recursivos definidos pelo utilizador.
Os casos de estudo correspondem a` implementacao de tabelas, ou func
oes finitas, em Haskell,
e ainda `a representac
ao de express
oes aritmeticas.

4.1

Implementac
ao de uma Tabela por uma Lista

Considere que se representa informacao relativa `a avaliacao dos alunos inscritos numa disciplina,
que tem no seu sistema de avaliac
ao uma componente teorica e uma componente pratica, pelos
seguintes tipos de dados. Observe-se que cada estudante pode ter ou nao ja obtido nota numa das
componentes da disciplina (te
orica ou pratica), o que se representa recorrendo a tipos Maybe.
type
type
type
type

Nome = String
Numero = Int
NT = Maybe Float
NP = Maybe Float

data Regime = Ordinario | TrabEstud


deriving (Show, Eq)
type Aluno = (Numero, Nome, Regime, NT, NP)
type Turma = [Aluno]
Tarefa 1
Defina as seguintes func
oes:
1. pesquisaAluno :: Numero -> Turma -> Maybe Aluno que efectua a pesquisa de um(a)
aluno(a) pelo seu n
umero de estudante. Observe que este e um identificador apropriado,
uma vez que todos os alunos tem n
umeros diferentes.
2. alteraNP :: Numero -> NP -> Turma -> Turma e
alteraNT :: Numero -> NT -> Turma -> Turma, que permitem alterar as notas pr
atica
e te
orica de um(a) estudante numa turma.
3. notaFinal :: Numero -> Turma -> Maybe Float que calcula a nota final de um aluno
segundo a f
ormula N F = 0.6N T + 0.4N P . Note que s
o e possvel calcular esta nota caso o
n
umero fornecido exista na turma, e ambas as componente NT e NP atinjam a nota mnima
de 9.5 valores.
4. trabs ::

Turma -> Turma que filtra os alunos trabalhadores-estudantes apenas.

5. aprovados :: Turma -> [(Nome, Float)] que apresenta as notas de todos os alunos
aprovados, i.e. com nota final superior ou igual a 10 valores.
Tarefa 2
A pesquisa nesta tabela torna-se mais eficiente se se mantiver a informac
ao ordenada. Sendo a
chave de pesquisa o n
umero de aluno, a informac
ao dever-se-
a manter ordenada segundo estre
criterio.
1. Escreva uma func
ao de inserc
ao ordenada de novos alunos numa turma,
insere :: Aluno -> Turma -> Turma.
21

2. Redefina a func
ao de pesquisa da tarefa anterior, tendo em conta que a tabela e representada
por uma lista ordenada.
Tarefa 3
Pretende-se agora distinguir os alunos que frequentam a disciplina pela primeira vez dos restantes
alunos. Estes u
ltimos poder
ao ter uma nota pr
atica congelada, obtida no ano lectivo anterior,
mas poder
ao optar por ser de novo avaliados na componente pr
atica no ano actual, devendo ser
guardadas ambas as notas. A nota pr
atica final ser
a a melhor das duas.
Utiliza-se para isto o seguinte tipo de dados definido pelo utilizador, com dois construtores:
data Aluno = Primeira (Numero, Nome, Regime, NT, NP)
| Repetente (Numero, Nome, Regime, NT, NP, NP)
deriving Show
Reescreva todas as func
oes das tarefas anteriores tendo em conta esta alterac
ao no tipo Aluno.

4.2

Implementac
ao de uma Tabela por uma Arvore
Bin
aria de Pesquisa

Relembre que estas


arvores se caracterizam pela seguinte propriedade (invariante de ordem): o
conte
udo de qualquer n
o situado `
a esquerda de um no X e necessariamente menor do que o conte
udo
de X, que por sua vez e necessariamente menor do que o conte
udo de qualquer no situado `a sua
direita. Admitindo-se a ocorrencia de elementos iguais, uma destas restricoes e relaxada para
menor ou igual.
Considere agora a seguinte definic
ao de um tipo de dados polimorfico para arvores binarias:
data BTree a = Empty | Node a (BTree a) (BTree a)
deriving Show
Para se utilizar uma
arvore bin
aria para implementar uma tabela de alunos, basta redefinir:
type Turma = BTree Aluno
Tarefa 4
Escreva as seguintes func
oes, cujo significado e o mesmo considerado na secc
ao anterior desta
ficha:
1. insere ::

Aluno -> Turma -> Turma

2. pesquisaAluno ::

Numero -> Turma -> Maybe Aluno

3. alteraNP ::
alteraNT ::

Numero -> NP -> Turma -> Turma e


Numero -> NT -> Turma -> Turma

4. notaFinal ::

Numero -> Turma -> Maybe Float

5. trabs :: Turma -> [Aluno] (note que os alunos trabalhadores-estudantes s


ao aqui apresentados numa lista)
6. aprovados ::

4.3

Turma -> [(Nome, Float)]

Arvores
de Express
ao

Considere os seguintes tipos de dados utilizados para a representacao de expressoes aritmeticas


por arvores bin
arias:
data OP = SOMA | SUB | PROD | DIV
deriving (Show, Eq)
data Expr = Folha Int | Nodo OP Expr Expr
deriving (Show, Eq)
22

Tarefa 5
1. Escreva uma func
ao aplica que aplica um operador bin
ario a dois argumentos inteiros:
aplica :: OP -> Int -> Int -> Int
2. Escreva uma func
ao avalia que procede ao c
alculo do valor de uma express
ao:
avalia :: Expr -> Int
3. Escreva finalmente uma func
ao imprime que produz uma string com a representac
ao usual
de uma express
ao representada por uma
arvore:
imprime :: Expr -> String

23

24

Ficha Pr
atica 5

Nesta ficha pretende-se que os alunos: consolidem os conceitos de classe, inst


ancia de classe e de
tipo qualificado; explorem algumas das classes pre-definidas mais usadas do Haskell (por exemplo:
Eq, Ord, Num, Show, Enum, ...) e definam novas classes.

5.1

Classes, Inst
ancias de Classes e Tipos Qualificados

As classes em Haskell permitem classificar tipos. Mais precisamente, uma classe estabelece um
conjunto de assinaturas de func
oes (os metodos da classe) cujos tipos que sao instancia dessa classe
devem ter definidas.
possvel que uma classe j
E
a forneca algumas funcoes definidas por omiss
ao. Caso uma funcao
nao seja definida explicitamente numa declaracao de instancia, o sistema assume a definicao por
omissao establecida na classe. Se existir uma nova definicao do metodo na declaracao de instancia,
sera essa definic
ao a ser usada.
Para saber informac
ao sobre uma determinada classe pode usar o comando do interpretador
ghci :i nome da classe . Em anexo apresenta-se um resumo de algumas das classes pre-definidas
mais utilizadas do Haskell.
Tarefa 1
Verifique qual e o tipo inferido pelo ghci (o tipo principal), para cada uma das seguintes func
oes
(pre-definidas): elem, sum e minimum. Justifique o tipo da func
ao, com base no que dever
a ser a
sua definic
ao.
Tarefa 2
Considere as seguintes declarac
oes de tipo usadas para representar as horas de um dia nos formatos
usuais.
data Part = AM | PM
deriving (Eq, Show)
data TIME = Local Int Int Part
| Total Int Int
1. Defina algumas constantes do tipo TIME.
2. Defina a func
ao totalMinutos ::
hora.

TIME -> Int que conta o total de minutos de uma dada

3. Defina TIME como inst


ancia da classe Eq de forma a que a iguladade entre horas seja independente do formato em que hora est
a guardada.
4. Defina TIME como inst
ancia da classe Ord.
5. Defina TIME como inst
ancia da classe Show, de modo a que a apresentac
ao dos termos (Local
10 35 AM), (Local 4 20 PM) e (Total 17 30) seja respectivamente: 10:35 am, 4:20 pm
e 17h30m.
6. Defina a func
ao seleciona :: TIME -> [(TIME,String)] -> [(TIME,String)] que recebe uma hora e uma lista de hor
arios de cinema, e seleciona os filmes que comecam depois
de uma dada hora.
7. Declare TIME como inst
ancia da classe Enum, de forma a que succ avance o rel
ogio 1 minuto
e pred recue o rel
ogio 1 minuto. Assuma que o sucessor de 11:59 pm e 00:00 am. Depois,
faca o interpretador calcular o valor das seguintes express
oes: [(Total 10 30)..(Total
10 35)] e [(Total 10 30),(Local 10 35 AM)..(Total 15 20)].
25

Tarefa 3
Considere as declarac
oes da classe FigFechada e da func
ao fun a seguir apresentadas
class FigFechada a where
area :: a -> Float
perimetro :: a -> Float
fun figs = filter (\fig -> (area fig) > 100) figs
1. Indique, justificado, qual e o tipo inferido pelo interpretador Haskell para a func
ao fun.
2. No plano cartesiano um rect
angulo com os lados paralelos aos eixos pode ser univocamente
determinado pelas coordenadas do vertice inferior esquerdo e pelos comprimentos dos lados,
ou por uma diagonal dada por dois pontos. Assim, para representar esta figura geometrica,
definiu-se em Haskell o seguinte tipo de dados:
type Ponto = (Float,Float)
type Lado = Float
data Rectangulo = PP Ponto Ponto
| PLL Ponto Lado Lado
Declare Rectangulo como inst
ancia da classe FigFechada.
3. Defina a func
ao somaAreas :: [Rectangulo] -> Float que calcula o somat
orio de uma
lista de rect
angulos. (De preferencia, utilize func
oes de ordem superior.)

5.2

Algumas das classes pr


e-definidas do Haskell

A classe Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
-- Minimal complete definition: (==) or (/=)
x == y = not (x /= y)
x /= y = not (x == y)

A classe Ord
data Ordering = LT | EQ | GT
deriving (Eq, Ord, Ix, Enum, Read, Show, Bounded)
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
x < y

= compare x y /= GT
= compare x y == LT

26

x >= y
x > y
max x y
min x y

= compare x y /= LT
= compare x y == GT
|
|
|
|

x <= y
otherwise
x <= y
otherwise

=
=
=
=

y
x
x
y

A classe Num
class (Eq a, Show a) => Num a where
(+), (-), (*) :: a -> a -> a
negate
:: a -> a
abs, signum
:: a -> a
fromInteger
:: Integer -> a
-- Minimal complete definition: All, except negate or (-)
x - y
= x + negate y
negate x
= 0 - x

A classe Show
class Show a where
show
:: a -> String
showsPrec :: Int -> a -> ShowS
showList :: [a] -> ShowS
-- Minimal complete definition: show or showsPrec
show x
= showsPrec 0 x ""
showsPrec _ x s = show x ++ s
showList []
= showString "[]"
showList (x:xs) = showChar [ . shows x . showl xs
where showl []
= showChar ]
showl (x:xs) = showChar , . shows x . showl xs

A classe Enum
class Enum a where
succ, pred
toEnum
fromEnum
enumFrom
enumFromThen
enumFromTo
enumFromThenTo

::
::
::
::
::
::
::

a -> a
Int -> a
a -> Int
a -> [a]
a -> a -> [a]
a -> a -> [a]
a -> a -> a -> [a]

-----

[n..]
[n,m..]
[n..m]
[n,n..m]

-- Minimal complete definition: toEnum, fromEnum


succ
= toEnum . (1+)
. fromEnum
pred
= toEnum . subtract 1 . fromEnum
enumFrom x
= map toEnum [ fromEnum x ..]
enumFromThen x y
= map toEnum [ fromEnum x, fromEnum y ..]
enumFromTo x y
= map toEnum [ fromEnum x .. fromEnum y ]
enumFromThenTo x y z = map toEnum [ fromEnum x, fromEnum y .. fromEnum z ]

27

28

Ficha Pr
atica 6

O objectivo desta ficha e a escrita de programas que envolvam IO. Concretamente, pretende-se
que os alunos construam um programa completo com interface por menus, e manipulacao de
ficheiros.
O caso de estudo e uma base de dados implementada numa arvore de binaria de procura.
Depois do programa ser codificado, propoem-se que o codigo seja compilado, criando um programa execut
avel. As func
oes haskell que estao descritas nesta ficha estao disponveis no ficheiro
ficha6.hs.

6.1

Input / Output em Haskell

Quando realizamos uma aplicac


ao, necessitamos de executar operacoes de entrada/saida de
dados. Estas operac
oes escapam `
a identificacao realizada no paradigma funcional de execucao
de um programa como o c
alculo do valor de uma expressao pretende-se antes especificar
acc
oes que devem ser realizadas numa dada sequencia. Como exemplos de operacoes input/output
podemos citar: ler um valor do teclado; escrever uma mensagem no ecran; ler/escrever um ficheiro
com dados.
Em Haskell, a integrac
ao destas operacoes e realizada por intermedio do monad IO. Podemos
entender o monad IO como uma marca que assinala que um valor de um dado tipo foi obtido
fazendo uso de operac
oes de entrada/saida. Assim, um valor do tipo IO Int pode ser entendido
como um programa que realiza operacoes entrada/saida e retorna um valor do tipo Int (ver
leInt no exemplo apresentado abaixo, onde e retornado um valor inteiro lido do teclado). Ora,
esta distinc
ao entre valores puros do tipo Int, e os valores obtidos por intermedio de operacoes
entrada/saida (tipo IO Int) coloca um problema evidente: se tivermos uma funcao que opere sobre
inteiros (por exemplo, a func
ao fact apresentada abaixo), ela nao pode ser directamente aplicada
a leInt::IO Int. Como podemos entao calcular o factorial de um valor introduzido no teclado?
A resposta a esta quest
ao encontra-se nas operacoes que caracterizam um Monad (estudadas na
teorica) na pr
atica, e prefervel utilizar a notacao do disponibilizada pela linguagem Haskell.
Chamemos computac
ao `
as expressoes cujo calculo envolve operacoes entrada/saida (i.e. expressoes do tipo IO t, qualquer que seja o tipo t). A notacao do permite definir uma computacao
como a sequenciac
ao de um conjunto de computacoes (o valor retornado e o da u
ltima). Mas nesta
sequencia vamos poder aceder aos valores que vao sendo calculados. Mais precisamente:
a seta <- permite-nos aceder ao valor retornado pela computacao correspondente. No exemplo apresentado abaixo, na linha l<-getLine, temos que getLine::IO String (e uma
func
ao pre-definida que le uma linha do teclado). Assim, l sera do tipo String e correponder
a ao texto introduzido pelo utilizador;
a operac
ao return permite-nos embeber um valor numa computacao. Ainda no exemplo
apresentado, ((read l)::Int) permite ler o inteiro da string l (a operacao inversa do
show). Assim, return ((read l)::Int) corresponde `a computacao que retorna esse valor.
leInt :: IO Int
leInt = do putStr "Escreva um n
umero: "
l <- getLine
return ((read l)::Int)
fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n-1)
prog1 :: IO ()
prog1 = do x <- leInt
putStrLn ("o factorial de "++(show x)++"
e "++(show (fact x)))
29

Estas declarac
oes, bem como as que sao apresentadas no resto desta ficha, estao dispoveis no
ficheiro ficha6.hs.
Vamos agora retomar o problema (das turmas) apresentado na ficha 4, para criar um programa
com interface por menus.

6.2

Declarac
ao de Tipos

Relembre da ficha pr
atica 4 o problema de manter numa arvore binaria de procura a informacao
sobre os alunos inscritos numa dada disciplina. Nessa ficha foram definidos os seguintes tipos de
dados (acrescentamos apenas as inst
ancias derivadas da classe Read para os tipos de dados Regime
e BTree).
type
type
type
type

Nome = String
Numero = Int
NT = Maybe Float
NP = Maybe Float

data Regime = Ordinario | TrabEstud


deriving (Show, Eq, Read)
type Aluno = (Numero, Nome, Regime, NT, NP)
data BTree a = Empty | Node a (BTree a) (BTree a)
deriving (Show, Read)
type Turma = BTree Aluno

6.3

Construc
ao de Menus

Vamos agora usar estas declarac


oes para construir um programa que mantem a informacao acerca
dos alunos. Vamos para isso usar o tipo IO x. Elementos deste tipo sao programas que fazem
algum Input/Output (i.e., escrevem coisas no ecran, leem do teclado, escrevem e leem ficheiros,
...) e que d
ao como resultado um elemento do tipo x.
O primeiro destes programas vai apenas apresentar (no ecran) uma lista das opcoes possveis
e retornar o valor escolhido pelo utilizador.
menu :: IO String
menu = do { putStrLn menutxt
; putStr "Opcao: "
; c <- getLine
; return c
}
where menutxt = unlines ["",
"Inserir Aluno .......
"Listar Alunos .......
"Procurar Aluno ......
"",
"Sair ................

1",
2",
3",
0"]

Vamos agora usar este programa para escrever um outro que, apos perguntar qual a opcao escolhida, invoca a func
ao correspondente. Note-se que neste caso, como de umas invocacoes para as
outras o estado (i.e., a informac
ao dos alunos) vai mudando, este estado deve ser um parametro.

30

ciclo :: Turma -> IO ()


ciclo t = do { op <- menu
; case op of
1:_ -> do { t <- insereAluno t
; ciclo t
}
2:_ -> do { listaAlunos t
; ciclo t
}
3:_ -> do { procuraAluno t
; ciclo t
}
0:_ -> return ()
otherwise -> do { putStrLn "Opcao invalida"
; ciclo t
}
}
Os programas insereAluno, listaAlunos, procuraAluno nao sao mais do que as extensoes para
IO das func
oes de insec
ao, listagem e pesquisa que definiu na ficha 5. Por exemplo, para o caso
da primeira,
insereAluno :: Turma -> IO Turma
insereAluno t
= do { putStr "\nNumero: "; nu <- getLine;
putStr "Nome: "; no <- getLine;
putStr "Regime: "; re <- getLine;
putStr "Nota Pratica: " ; np <- getLine;
putStr "Nota Teorica: "; nt <- getLine;
let reg = if re=="TE" then TrabEstud else Ordinario
pra = if np=="" then Nothing else Just ((read np)::Float)
teo = if nt=="" then Nothing else Just ((read nt)::Float)
in return (insere ((read nu),no,reg,pra,teo) t)
}
Tarefa 1
Defina os programas listaAlunos e procuraAluno.
Tarefa 2
1. Acrescente ao menu opc
oes para alterar as notas de um aluno, e defina as func
oes correspondentes.
2. Faca agora o importac
ao do m
odulo IO e crescente ainda ao seu programa a seguite func
ao
main
import IO
main = do { hSetBuffering stdout NoBuffering
; ciclo Empty
}
Note que o programa main arranca com a base de dados vazia (ou seja, a
arvore vazia). A
primeira linha do main e apenas um comando para forcar a visualizac
ao imediata daquilo
que e enviado para o ecran.
31

6.4

Manipulac
ao de Ficheiros

Para alem de escrever e ler dados no ecran e do teclado, e possvel consultar e escrever informacao
em ficheiro.
A forma mais elementar de aceder a um ficheiro e atraves da leitura e escrita do conte
udo do
ficheiro (visto como uma u
nica String). Para isso usam-se as funcoes readFile e writeFile.
Note que, como na definic
ao dos tipos Regime e BTree a optamos por declarar instancias
derivadas das classes Show e Read, podemos usar as funcoes show e read para fazer a conversao
entre estes tipos e o tipo String.
Por exemplo, para a escrita e a leitura de uma turma podemos escrever os seguintes programas:
writeTurma :: Turma -> IO ()
writeTurma t = do putStr "Nome do ficheiro: "
f <- getLine
writeFile f (show t)
readTurma :: IO Turma
readTurma = do putStr "Nome do ficheiro: "
f <- getLine
s <- readFile f
return (read s)
Temos agora todos os ingredientes para estender o programa acima dando-lhe a possibilidade
de guardar e ler os dados de um ficheiro (usando, entre outras as as funcoes read e show).
Tarefa 3
1. Acrescente aos programas menu e ciclo os itens necess
arios para estas duas operac
oes.
2. Experimente o programa que acabou de definir. Insira novos alunos numa turma, guarde essa
turma em ficheiro, verifique se o conte
udo do ficheiro que criou e o esperado. Depois saia
do programa, carregue a turma que est
a guardada em ficheiro, e acrescente interactivamente
novos alunos. Guarde a nova turma um outro ficheiro e verifique o seu conte
udo.
Por vezes temos que manipular ficheiros de uma forma mais elaborada.
Tarefa 4
Suponha que os Servicos Academicos formecem informac
ao sobre os alunos inscritos a uma disciplina em ficheiros de texto com o seguinte formato:
mero regime nome, com um aluno por linha, e em que regime apenas pode ser ORD ou
nu
TE. Por exemplo,
54321
33333
44111
22233

ORD
TE
TE
ORD

Ana Maria Santos Silva


Paulo Ferreira
Pedro Moura Gomes
Tiago Miguel de Sousa

Acrescente ao seu programa, uma func


ao que permita ler o ficheiro dos alunos inscritos e carregue
sua base de dados com essa informac
ao (criando uma turma ainda sem notas).
Tarefa 5
Pretende-se agora escrever em ficheiro a pauta final da disciplina (para posteriormente se imprimir,
por exemplo). Na pauta deve constar o n
umero do aluno, o seu nome e a nota final. A nota final
pode ser um valor inteiro entre 10 e 20, ou Rep, indicando que o aluno est
a reprovado. Por
exemplo,
32

54321
33333
44111
22233

Ana Maria Santos Silva


Paulo Ferreira
Pedro Moura Gomes
Tiago Miguel de Sousa

15
Rep
17
12

1. Acrescente ao seu programa uma func


ao que permita gravar pautas em ficheiro.
2. Adapte a sua resposta `
a alinea anterior de forma a que a pauta tenha os alunos ordenados
por ordem alfabetica.

6.5

Uma vers
ao melhorada do programa

Numa arvore bin


aria de procura, a pesquisa de informacao e particularmente eficiente se essa
arvore for balanceada (equilibrada). Os algoritmos de insercao que estudamos nao garantem que
a arvore de procura que obtemos seja balanceada. No entanto, podemos pelo menos garantir que
obtemos uma
arvore balaceada no momento em que carregamos a base de dados de ficheiro. Para
esse fim, vamos gravar uma turma em ficheiro como uma lista ordenada de alunos e, ao recuperar
a turma de ficheiro, montamos a turma na base de dados como uma arvore balanceada.
Tarefa 6
1. Defina uma func
ao que permita gravar uma turma num ficheiro como uma sequencia ordenada (por n
umero) de alunos.
2. Defina uma func
ao que dada uma lista ordenada cria uma
arvore balanceada com os elementos dessa lista.
3. Defina uma func
ao que permita ler o ficheiro (com a sequencia de alunos) gerado pela func
ao
da alnea 1, e construa uma
arvore de procura balanceada com esses alunos.
4. Acrescente aos programas menu e ciclo os itens necess
arios para disponibilizar estas operac
oes
de gravac
ao para ficheiro e carregamento para
arvore balanceada.
Pretende-se agora enriquecer o programa com nova funcionalidade.
Tarefa 7
1. Defina uma func
ao que permita remover um aluno (dado o seu n
umero) de uma turma, e
disponibilize no menu a opc
ao de remover um aluno da turma.
2. Acrescente ao seu programa qualquer outra funcionalidade que lhe pareca u
til.

6.6

Compilac
ao de um programa Haskell

Os programas que temos criado tem sido sempre interpretados com o auxilio do interpretador
GHCI. Uma forma alternativa de correr o programa e compilando-o por forma a obter uma
ficheiro execut
avel, que pode ser invocado ao nvel da shell do sistema operativo. Para isso e
necessario utilizar o compilador de Haskell, GHC.
Para criar programas execut
aveis o compilador Haskell precisa de ter definido um modulo
Main com uma func
ao main que tem que ser de tipo IO . A funcao main e o ponto de entrada no
programa, pois e ela que e invocada quando o programa compilado e executado.
A compilac
ao de um programa Haskell, usando o Glasgow Haskell Compiler, pode ser feita
executando na shell do sistema operativo o seguinte comando:

ghc -o nome do executavel --make nome do ficheiro do modulo principal

33

Tarefa 8
1. Compile o programa de forma a disp
or de um programa execut
avel. (Note que e necess
ario
que o nome do m
odulo onde se encontra definido o programa main se chame Main.)
2. Abra uma shell do sistema operativo e invoque o programa execut
avel que foi criado pela
compilac
ao.

34

Você também pode gostar