Este documento apresenta um resumo sobre programação funcional em Haskell. Ele discute o editor WinHugs, tipos de dados, definições de funções, escopo e tratamento de exceções.
2. 0. Sumário
Editor WinHugs
Nomes
Avaliação de funções
Definições dos tipos das funções
Associatividade
Tipos de Dados
Escopo
Definições Locais
Tratamento de Exceções
Prova de Programas
3. 1. Editor WinHugs
:? – comando Help (o princípio de tudo)
:? 2 + 3 <enter>
5
:? (1 * 6) == (3 ‘div’ 5) <enter>
False
:? maximo (quadrado 2) 1 <enter>
4
Palavras reservadas:
case else infix module type
class hiding infixl of where
data if infixr renaming
default import instance then
deriving in let to
4. 1. Editor WinHugs (exemplo.hs)
{-##############################################
exemplo.hs
Este arquivo eh um exemplo de um arquivo .hs. Os programas aqui
mostrados podem ser conseguidos na pagina de Haskell:
http://www.haskell.org que contem links importantes para quem
quer fazer parte de listas de usuarios de Haskell em todo mundo.
###############################################-}
resposta :: Int -- Uma constante inteira
resposta = 42
novalinha :: Char
novalinha = ’n’
sim :: Bool
sim = True
5. 1. Editor WinHugs (exemplo.hs)
maior :: Bool
maior = (resposta > 71)
quadrado :: Int -> Int
quadrado x = x*x
todosIguais :: Int -> Int -> Int -> Bool
todosIguais n m p = (n == m) && (m == p)
{-##########################-}
6. 1. Editor WinHugs
Principais comandos de Hugs
Comando Ação realizada
:? Aciona o help
:e Chama o script atual
:e exemplo.hs Edita o arquivo exemplo.hs
:l exemplo.hs Carrega o script exemplo.hs e limpa outros arquivos carregados
:a exemplo.hs Carrega o script exemplo.hs sem limpar os outros arquivos
:q Termina a sessão
Ver Prelude.hs;
Roteiro:
◦ Criar código-fonte: *.hs
◦ Carregar no WinHugs (:l ou :load)
◦ Executar suas funções
7. 1. Editor WinHugs
Comentários no código:
◦ com “- -”, que representa um comentário até o final da linha
corrente;
◦ ou com os símbolos “{-” e “-}” envolvendo todo o texto a se
tornar um comentário, que podem englobar até várias linhas
Uma função, em Haskell, pode ser representada
graficamente por uma caixa que recebe um ou mais
parâmetros como entrada, processa estes
parâmetros e constrói um resultado que é único.
A aplicação de uma função consiste no processo se
colocar os parâmetros na função.
resultado
parâmetro(s) função
8. 2. Nomes
Os nomes em Haskell são sensíveis a caracteres
◦ as letras maiúsculas são distintas das letras minúsculas.
Os nomes, também chamados de identificadores, devem
ser iniciados
◦ sempre por uma letra maiúscula, se for um tipo,
◦ ou minúscula, se for uma variável ou o nome de alguma função.
Estes nomes são seguidos, opcionalmente, por uma seqüência
de letras maiúsculas ou minúsculas, dígitos, sublinhado ou
acentos agudos.
As palavras reservadas da linguagem são sempre escritas em
letras minúsculas.
type Par = (Int, Int)
somaAmbos :: Par -> Int
somaAmbos (primeiro, segundo) = primeiro + segundo
9. 3. Avaliação de funções
leftmost-outermost
avaliação preguiçosa (lazy evaluation)
◦ só avalia uma expressão se ela for realmente
necessária e no máximo uma vez (avaliação
curto-circuito).
Testar:
todosIguais (quadrado 3) resposta (quadrado 2)
11. 4. Definições dos tipos das funções
Um tipo é uma coleção de valores onde todos eles têm as mesmas
características.
◦ Por exemplo, os números inteiros, os quadros, os caracteres, os strings de
caracteres, etc.
Em Haskell, se incentiva que todos os parâmetros de entrada e a
saída tenham seus tipos explicitados pelo programador como uma
forma de facilitar a checagem de tipos e de evitar muitos erros de
programação.
◦ No entanto, os tipos podem também ser inferidos a partir das definições das
funções.
+ :: Int -> Int -> Int e
escala :: Quadro -> Int -> Quadro
Int Int
Int +
Quadro Quadro
escala
Int
13. 5.1. Testes
eZero :: Int -> Bool
eZero 0 = True
eZero _ = False
fat :: Int -> Int
fat 0 = 1
fat n = n * fat (n -1)
14. 6. Tipos de dados
Admite:
◦ tipos primitivos e;
◦ tipos estruturados;
tipos definidos pelo usuário a partir de outros
tipos.
Os tipos primitivos:
◦ inteiro (Int ou Integer), booleano (Bool),
caractere (Char), cadeia de caracteres
(String), ponto flutuante (Float ou Double)
e o tipo lista.
15. 6.1. Tipo Inteiro
Os valores do tipo Integer são representados com o dobro da
quantidade de bits necessários para representar os valores do tipo
Int.
Operadores aritméticos para valores dos tipos
+, * adição e multiplicação
ˆ exponenciação
- subtração (infixa) e inversor de sinal (prefixa)
div divisão inteira (prefixa), ou ‘div‘ (infixa)
mod módulo (prefixa), ou ‘mod‘ (infixa)
abs valor absoluto de um inteiro
negate troca o sinal de um inteiro
Operadores relacionais: Int −> Int −> Bool
>, >=, ==, / =, <=, <
16. 6.1. Tipo Inteiro
Exemplo: série de fibonacci
0, 1, 1, 2, 3, 5, 8, 13, ...
fibonacci :: Int -> Int
fibonacci 1 = 0
fibonacci 2 = 1
fibonacci n = fibonacci(n-1) + fibonacci(n-2)
mdc :: Int -> Int -> Int
mdc n m
|m == n = n
|m > n = mdc m n
|otherwise = mdc (n - m) m
17. 6.2. Tipo Bool
Os únicos valores booleanos são True e False
Função Nome Tipo
&& and && :: Bool − > Bool − > Bool
|| or || :: Bool − > Bool − > Bool
not inversor not :: Bool − > Bool
A função Ou exclusivo
exOr :: Bool -> Bool -> Bool
exOr True x = not x
exOr False x = x
18. 6.3. O tipo caractere
São literais escritos entre aspas simples.
Caracteres especiais para utilizações específicas
‘t’ tabulação ‘” aspas simples
‘n’ nova linha ‘”’ aspas duplas
‘’ uma barra invertida ‘34’ ?
funções pré-definidas em Haskell feitas para
converter caracteres em números e vice-versa.
◦ toEnum :: Int − > Char
◦ fromEnum :: Char − > Int
19. 6.4. O tipo cadeia de caracteres
Os criadores de Haskell admitiram duas
formas para este tipo:
◦ É um tipo predefinido;
podem ser escritos entre aspas duplas
“Constantino”
◦ Ou pode ser considerado como uma lista de
caracteres
Descrita como type String = [Char]
usando a notação de lista de caracteres (entre aspas
simples).
[’C’, ’o’, ’n’, ’s’, ’t’, ’a’, ’n’, ’t’, ’i’, ’n’, ’o’].
20. 6.4. O tipo cadeia de caracteres
Exemplos:
◦ “baleia”− > baleia
◦ −>
◦ “99a116”− > cat
◦ “jeri”++ “qua”++ “quara”− > jeriquaquara
Exercício:
◦ Defina uma função charToName :: Char − > Int que converte
um dígito em seu valor (por exemplo, ‘8’ em 8). O valor de um
caractere não dígito deve ser 0 (zero).
21. 6.5. O tipo ponto flutuante
Os valores do tipo ponto flutuante (números reais) pertencem aos
tipos Float ou Double;
A diferenças entre os valores destes tipos se verificam na
quantidade de bits necessários para representá-los.
22. 6.6. Os tipos de dados estruturados
Os tipos estruturados são construídos a partir
de outros tipos, sejam eles primitivos ou
estruturados.
Tipo produto cartesiano:
◦ representado em Haskell pelas tuplas;
◦ representado registros ou estruturas em linguagens
imperativas
◦ o tipo (t1, t2, ..., tn) consiste de tuplas de valores (v1,
v2, ..., vn) onde v1 :: t1, v2 :: t2, ..., vn :: tn
23. 6.6. Os tipos de dados estruturados
Exemplo:
◦ type Pessoa = (String, String, Int)
◦ maria :: Pessoa
◦ maria = ("Maria das Dores", "225-0000", 22)
◦ intP :: (Int, Int)
◦ intP = (35, 45)
◦ somaPar :: (Int, Int) -> Int
◦ somaPar (x, y) = x + y
◦ shift :: ((Int, Int), Int) -> (Int, (Int, Int))
◦ shift ((a,b),c) = (a, (b,c))
24. 6.6. Os tipos de dados estruturados
Mais exemplos:
◦ nome :: Pessoa -> String
◦ fone :: Pessoa -> String
◦ idade :: Pessoa -> Int
◦ nome (n, p, a) = n
◦ fone (n, p, a) = p
◦ idade (n, p, a) = a
◦ somaPar :: (Int, Int) -> Int
Requer um argumento: tupla
◦ somaPar (a, b) = a + b
Cuidado, são diferentes!!!
◦ somaDois :: Int -> Int -> Int
◦ somaDois a b = a + b Requer dois argumentos
25. 7. Escopo
O escopo de uma definição é a parte de um programa
na qual ela é visível e portanto pode ser usada.
Em Haskell, o escopo das definições é todo o script.
ehImpar, ehPar :: Int -> Bool
ehImpar 0 = False
ehImpar n = ehPar (n-1)
ehPar 0 = True
ehPar n = ehImpar (n-1)
26. 8. Definições Locais
Através de palavras reservadas: where, let, in, if, then,
else, ...
somaQuadrados :: Int -> Int -> Int
somaQuadrados n m = quadN + quadM
where
quadN = n * n
quadM = m * m
let x = 3 + 2; y = 5 - 1 in x^2 + 2*x*y - y
27. 8. Definições Locais
As definições locais são visíveis apenas na equação onde elas
foram declaradas. As variáveis que aparecem do lado
esquerdo também podem ser usadas em definições locais.
maxq :: Int -> Int -> Int
maxq x y
|sqx > sqy = sqx
|otherwise = sqy
where
sqx = sq x
sqy = sq y
sq :: Int -> Int
sq z = z * z
28. 8. Definições Locais
As definições locais podem ser usadas antes que elas sejam
definidas e também podem ser usadas em resultados, em guardas
ou em outras definições locais.
maxThreeOccurs :: Int -> Int -> Int -> (Int, Int)
maxThreeOccurs n m p = (max, eqCount)
where
max = maxiThree n m p
eqCount = equalCount max n m p
maxiThree :: Int -> Int -> Int -> Int
maxiThree a b c = maximo (maximo (a, b), c)
equalCount :: Int -> Int -> Int -> Int -> Int
equalCount val n m p equalCount val n m p
= isN + isM + isP = isval n + isval m + isval p
where where
isN = if n == val then 1 else 0 isval :: Int − > Int
isM = if m == val then 1 else 0 isval x = if x == val then 1 else 0
isP = if p == val then 1 else 0
29. 8.1 Cálculo
Um exemplo bastante conhecido é o de encontrar as
raízes reais de uma equação do segundo grau. Neste
caso teremos como entrada a equação a * xˆ2 + b * x +
c = 0.
Para construirmos esta solução, a saída será a string “A
equação 1.0 * x^2 + 5.0 * x + 6.0= 0.0 tem duas raízes
reais e distintas: -2.0 e -3.0”.
Para isto vamos construir duas funções, sendo uma para
o caso da função ter duas raízes reais e distintas e a
outra para o caso dela ter duas raízes reais e iguais.
30. 8.1 Cálculo
umaRaiz :: Float -> Float -> Float -> Float
umaRaiz a b c = -b / (2.0 * a)
duasRaizes :: Float -> Float -> Float -> (Float, Float)
duasRaizes a b c = (d + e, d - e)
where
d = -b/(2.0*a)
e = sqrt (b^2 - 4.0*a*c)/(2.0*a)
saida :: Float -> Float -> Float -> String
saida a b c = cabecalho a b c ++ raizes a b c
31. 8.1 Cálculo
cabecalho :: Float -> Float -> Float -> String
cabecalho a b c = "A equacao nnt"++ show a ++ "*x^2 + " ++
show b ++ "*x + " ++ show c ++ " = 0.0" ++ "nntem "
raizes:: Float -> Float -> Float -> String
raizes a b c
| b^2 > 4.0*a*c = "duas raizes reais e distintas: " ++ show f ++ "
e " ++ show s
| b^2 == 4.0*a*c = "duas raizes reais e iguais: " ++ show
(umaRaiz a b c)
| otherwise = "nenhuma raiz real "
where (f, s) = duasRaizes a b c
32. 9. Tratamento de Exceções
O tratamento de exceções é uma forma de se programar
que previne a ocorrência de erros devido a entradas não
previstas
◦ ao tempo em que indica quais as providências que o computador deve
tomar no caso dessas entradas ocorrerem
◦ sem ter que necessariamente abortar o programa ou deixar que ele
entre em loops infinitos até extinguir toda a memória.
No caso da equação do segundo grau, quando a entrada para
o coeficiente a for zero, não será possível a divisão de
qualquer número por ele.
umaRaiz a b c
|(a /= 0.0) = -b/ (2.0 * a)
|otherwise = error "umaRaiz chamada com a == 0"
33. 10. Prova de Programas
Uma prova é uma argumentação lógica ou matemática para
verificar se alguma premissa é ou não válida em quaisquer
circunstâncias.
Este tema tem importância fundamental na construção de
programas, uma vez que deve-se ter a garantia de que o
programa esteja correto e que ele resolva exatamente o problema
para o qual foi codificado.
No entanto, existe um dilema: aversão pela prova de programas.
Por se acreditar que é uma técnica de fundamentação matemática
e, portanto, teórica.
A prova de programas em qualquer linguagem imperativa é
realmente tediosa, no entanto, ela se torna quase natural em
uma linguagem funcional como Haskell.
34. 10. Prova de Programas
No entanto, um cuidado deve ser tomado,
para que se evitem alguns erros de
avaliação. A avaliação de uma expressão
pode:
◦ parar e dar uma resposta (totalVendas 2).
O valor da expressão é definido, ou
◦ nunca parar (totalVendas (-2)).
O valor da expressão é indefinido.
Qual o valor de 0*e?
35. 10. Prova de Programas
Existem em Haskell três formas de se
formalizar provas: a prova direta, a prova
por casos e a prova por indução
matemática.
10.1. Prova Direta
◦ A verificação da prova direta é feita pela
aplicação das definições das funções.
36. 10.1. Prova Direta
troca :: (Int, Int) -> (Int, Int)
troca (a, b) = (b, a)
cicla, recicla :: (Int, Int, Int) -> (Int, Int, Int)
cicla (a, b, c) = (b, c, a)
recicla (a, b, c) = (c, a, b)
troca (troca (a, b)) = (a, b)?
cicla (recicla (a, b, c)) = recicla (cicla (a, b, c))?
37. 10.2. Prova por Casos
maximo :: Int -> Int -> Int
maximo n m
|n >= m = n
|otherwise = m
Assertiva: “Para quaisquer números n e m, maximo n m >= n”?.
Para quaisquer números m e n definidos, tem-se: m > n ou n >= m.
Então, se n >= m: maximo n m = n. Portanto, maximo n m >= n.
Se m > n: maxi n m = m e m > n. Portanto, maximo n m > n.
Logo, maximo n m >= n.
38. 10.3. Prova por Indução Matemática
10.3.1. Indução Matemática
◦ Sistemas de Provas -> Especificação Formal
◦ Para provar que uma propriedade P(n) é
válida para todo natural n, deve-se:
Caso base: Provar P(n), para n = 0.
Passo indutivo: Para n > 0, provar P(n), assumindo
que P(n-1) é válida.
fatorial 0 = 1 -- (fat 1)
fatorial n = n * fatorial (n - 1) -- (fat 2)
Como provar?
39. 10.3. Prova por Indução Matemática
Podemos agora provar a seguinte propriedade
dos naturais:
◦ P(n): fatorial n > 0, para todo natural n.
O esquema de prova é feito da seguinte forma:
◦ Caso base (P(0)): fatorial 0 = 1 (por fat 1) e 1 > 0. Logo
fatorial 0 > 0.
◦ Passo indutivo (P(n)): fatorial n = n * fatorial (n-1), (por
fat 2) admitindo-se que n>0. A hipótese de indução
informa que fatorial (n-1) > 0, ou seja, a propriedade P é
válida para n-1.
40. 10.3. Prova por Indução Matemática
Assim o fatorial de n é o produto de dois
fatores sendo ambos maiores que zero, ou
seja, temos >0 * >0. O produto de dois
números positivos é também positivo. Logo,
maior que 0.
Como a propriedade P é válida para o caso
base e para o passo indutivo, então ela é
válida para todo n natural. Esta última parte
é a conclusão da prova.
41. 10.3. Prova por Indução Matemática
10.3.2. Prova por Indução
◦ Enquanto uma indução formula provas para P(0), P(1), ..., a
definição recursiva constrói resultados para fatorial 0, fatorial 1,
....
◦ Esta forma de prova normalmente é aplicada à funções definidas
por recursão primitiva uma vez que representa tão somente um
processo de tradução;
◦ Guia de passos a ser seguido nos esquemas de provas por
indução em Haskell [Simon Thompson]
Estágio 0: escrever o objeto da prova em linguagem natural,
Estágio 1: escrever o objeto da prova em linguagem formal,
Estágio 2: escrever os sub-objetos da prova por indução:
P(0):
P(n), para todo n>0, assumindo P(n-1)
Estágio 3: Provar P(0)
Estágio 4: Provar P(n), para n>0, lembrando que deve e pode usar P(n-1)
42. 10.3. Prova por Indução Matemática
Exemplo:
power2 :: Int −> Int
power2 0 = 1 (1)
power2 r = 2 * power2 (r - 1) (2)
sumPowers :: Int − > Int
sumPowers 0 = 1 (3)
sumPowers r = sumPowers (r-1) + power2 r (4)
Provar que sumPowers n + 1 = power2 (n + 1).
43. 10.3. Prova por Indução Matemática
Estágio 0: provar que a soma das potências de 2 de 0 a n,
adicionada a 1 é igual a (n + 1)-ésima potência de 2.
Estágio 1: provar P(n): sumPowers n + 1 = power2 (n+1)
Estágio 2: sumPowers 0 + 1 = = power2 (0 + 1), para n = 0?
sumPowers n + 1 = = power2 (n + 1), para n > 0?,
assumindo que sumPowers (n - 1) + 1 = power2 n
Estágio 3: sumPowers 0 + 1 = 1 + 1 = 2 –por (3)
power2 (0 + 1) = 2 * power2 0 = 2 * 1 = 2 –por (2)
logo, a prova é válida para o caso base.
sumPowers n + 1 = sumPowers (n-1) + power2 n + 1 –por (4)
= sumPowers(n-1) + 1 + power2 n –pela comutatividade de +
= power2 n + power2 n –pela hip. de indução
= 2 * power2 n
= power2 (n+1) –por (2)
44. 10.3. Prova por Indução Matemática
Exercícios:
◦ Prove que, para todo número natural n,
fac (n + 1) >= power2 n.
◦ Prove que, para todo número natural n,
fib (n+1) >= power2 (n div 2).
45. Resumo
Em Haskell, uma função é uma operação que
transforma suas entradas em uma saída.
Um tipo é uma coleção de objetos similares.
Todo objeto deve ter um tipo claramente
definido.
As funções definidas em um programa em
Haskell podem ser usadas na avaliação de
expressões.
Tipos de dados primitivos e as tuplas.
Exceções e provas.