unidade7 1
TRANSCRIPT
Programação Funcional com HaskellProf. Iális CavalcanteFonte: Prof. Dr. Francisco Vieira
Engenharia da Computação – UFC/Sobral
0. SumárioEditor WinHugsNomes
Avaliação de funçõesDefinições dos tipos das funçõesAssociatividadeTipos de DadosEscopoDefinições LocaisTratamento de ExceçõesProva de Programas
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 typeclass hiding infixl of wheredata if infixr renamingdefault import instance thenderiving in let to
1. Editor WinHugs (exemplo.hs){-##############################################
exemplo.hs
Este arquivo eh um exemplo de um arquivo .hs. Os programas aquimostrados podem ser conseguidos na pagina de Haskell:http://www.haskell.org que contem links importantes para quemquer 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
1. Editor WinHugs (exemplo.hs)maior :: Boolmaior = (resposta > 71)quadrado :: Int -> Intquadrado x = x*xtodosIguais :: Int -> Int -> Int -> BooltodosIguais n m p = (n == m) && (m == p){-##########################-}
1. Editor WinHugs
Ver Prelude.hs;Roteiro:◦ Criar código-fonte: *.hs
◦ Carregar no WinHugs (:l ou :load)
◦ Executar suas funções
Principais comandos de HugsComando 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
1. Editor WinHugsComentá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 setornar um comentário, que podem englobar até várias linhas
Uma função, em Haskell, pode ser representadagraficamente por uma caixa que recebe um ou maisparâmetros como entrada, processa estesparâmetros e constrói um resultado que é único.A aplicação de uma função consiste no processo secolocar os parâmetros na função.
funçãoresultado
parâmetro(s)
2. NomesOs 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 -> IntsomaAmbos (primeiro, segundo) = primeiro + segundo
3. Avaliação de funções
leftmost-outermostavaliaçã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)
3. Avaliação de funções (cont.)
todosIguais (quadrado 3) resposta (quadrado 2)
todosIguais (quadrado 3) resposta (quadrado 2)= ((quadrado 3) == resposta) && (resposta == (quadrado 2))= ((3 * 3) == resposta) && (resposta == (quadrado 2))= (9 == resposta) && (resposta == (quadrado 2))= (9 == 42) && (42 == (quadrado 2))= False && (42 == (quadrado 2)) (utilizando lazy evaluation)= False
4. Definições dos tipos das funçõesUm tipo é uma coleção de valores onde todos eles têm as mesmascaracterí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 asaída tenham seus tipos explicitados pelo programador como umaforma de facilitar a checagem de tipos e de evitar muitos erros deprogramaçã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
+IntInt
escalaQuadroQuadro
Int
Int
5. Associatividade
Prioridade Assoc. à esquerda Não associativa Assoc. `a direita
9 !, !!, //, > . > >>= .
8 **, ˆ, ˆˆ
7 % , /, ‘div‘, ‘mod‘, ‘rem‘, ‘quot‘
6 +, - :+
5 \\ :, ++, > + >
4 /=, <, <=, = =, >, >=,‘elem‘, ‘notElem‘
3 &&
2 ||
1 :=
0 $
5.1. Testes
eZero :: Int -> BooleZero 0 = TrueeZero _ = False
fat :: Int -> Intfat 0 = 1fat n = n * fat (n -1)
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.
6.1. Tipo InteiroOs 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
>, >=, ==, / =, <=, <
6.1. Tipo InteiroExemplo: 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
6.2. Tipo BoolOs únicos valores booleanos são True e False
A função Ou exclusivoexOr :: Bool -> Bool -> BoolexOr True x = not xexOr False x = x
Função Nome Tipo
&& and && :: Bool − > Bool − > Bool
|| or || :: Bool − > Bool − > Bool
not inversor not :: Bool − > Bool
6.3. O tipo caractereSão literais escritos entre aspas simples.
funções pré-definidas em Haskell feitas para converter caracteres em números e vice-versa.◦ toEnum :: Int − > Char◦ fromEnum :: Char − > Int
Caracteres especiais para utilizações específicas
‘\t’ tabulação ‘\” aspas simples
‘\n’ nova linha ‘\”’ aspas duplas
‘\\’ uma barra invertida ‘\34’ ?
6.4. O tipo cadeia de caracteresOs 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’].
6.4. O tipo cadeia de caracteres
Exemplos:◦ “baleia”− > baleia◦ − >◦ “\99a\116”− > 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 umcaractere não dígito deve ser 0 (zero).
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
6.6. Os tipos de dados estruturadosExemplo:◦ 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))
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
◦ somaPar (a, b) = a + b
◦ somaDois :: Int -> Int -> Int
◦ somaDois a b = a + b
Cuidado, são diferentes!!!
Requer um argumento: tupla
Requer dois argumentos
7. EscopoO 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 -> BoolehImpar 0 = FalseehImpar n = ehPar (n-1)
ehPar 0 = TrueehPar n = ehImpar (n-1)
8. Definições LocaisAtravés de palavras reservadas: where, let, in, if, then, else, ...
somaQuadrados :: Int -> Int -> IntsomaQuadrados n m = quadN + quadM
wherequadN = n * nquadM = m * m
let x = 3 + 2; y = 5 - 1 in x^2 + 2*x*y - y
8. Definições LocaisAs 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 -> Intmaxq x y
|sqx > sqy = sqx|otherwise = sqy
wheresqx = sq xsqy = sq ysq :: Int -> Intsq z = z * z
8. Definições LocaisAs 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)
wheremax = maxiThree n m peqCount = equalCount max n m pmaxiThree :: Int -> Int -> Int -> IntmaxiThree a b c = maximo (maximo (a, b), c)equalCount :: Int -> Int -> Int -> Int -> Int
equalCount val n m p= isN + isM + isP
whereisN = if n == val then 1 else 0isM = if m == val then 1 else 0isP = if p == val then 1 else 0
equalCount val n m p= isval n + isval m + isval p
whereisval :: Int − > Intisval x = if x == val then 1 else 0
8.1 CálculoUm 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.
8.1 CálculoumaRaiz :: Float -> Float -> Float -> FloatumaRaiz a b c = -b / (2.0 * a)
duasRaizes :: Float -> Float -> Float -> (Float, Float)duasRaizes a b c = (d + e, d - e)
whered = -b/(2.0*a)e = sqrt (b^2 - 4.0*a*c)/(2.0*a)
saida :: Float -> Float -> Float -> Stringsaida a b c = cabecalho a b c ++ raizes a b c
8.1 Cálculocabecalho :: Float -> Float -> Float -> Stringcabecalho a b c = "A equacao \n\n\t"++ show a ++ "*x^2 + " ++
show b ++ "*x + " ++ show c ++ " = 0.0" ++ "\n\ntem "
raizes:: Float -> Float -> Float -> Stringraizes 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
9. Tratamento de ExceçõesO 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"
10. Prova de ProgramasUma 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.
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?
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.
10.1. Prova Diretatroca :: (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))?
10.2. Prova por Casosmaximo :: 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.
10.3. Prova por Indução Matemática10.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?
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.
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.
10.3. Prova por Indução Matemática10.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)
10.3. Prova por Indução Matemática
Exemplo:power2 :: Int −> Intpower2 0 = 1 (1)power2 r = 2 * power2 (r - 1) (2)
sumPowers :: Int − > IntsumPowers 0 = 1 (3)sumPowers r = sumPowers (r-1) + power2 r (4)
Provar que sumPowers n + 1 = power2 (n + 1).
10.3. Prova por Indução MatemáticaEstá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)
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).
ResumoEm Haskell, uma função é uma operação quetransforma suas entradas em uma saída.Um tipo é uma coleção de objetos similares.Todo objeto deve ter um tipo claramentedefinido.As funções definidas em um programa emHaskell podem ser usadas na avaliação deexpressões.Tipos de dados primitivos e as tuplas.Exceções e provas.