unidade7 1

45
Programação Funcional com Haskell Prof. Iális Cavalcante Fonte: Prof. Dr. Francisco Vieira Engenharia da Computação – UFC/Sobral

Upload: ialis-cavalcante

Post on 09-Jul-2015

950 views

Category:

Education


4 download

TRANSCRIPT

Page 1: Unidade7 1

Programação Funcional com HaskellProf. Iális CavalcanteFonte: Prof. Dr. Francisco Vieira

Engenharia da Computação – UFC/Sobral

Page 2: Unidade7 1

0. SumárioEditor WinHugsNomes

Avaliação de funçõesDefinições dos tipos das funçõesAssociatividadeTipos de DadosEscopoDefinições LocaisTratamento de ExceçõesProva de Programas

Page 3: Unidade7 1

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

Page 4: Unidade7 1

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

Page 5: Unidade7 1

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){-##########################-}

Page 6: Unidade7 1

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

Page 7: Unidade7 1

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)

Page 8: Unidade7 1

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

Page 9: Unidade7 1

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)

Page 10: Unidade7 1

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

Page 11: Unidade7 1

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

Page 12: Unidade7 1

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 $

Page 13: Unidade7 1

5.1. Testes

eZero :: Int -> BooleZero 0 = TrueeZero _ = False

fat :: Int -> Intfat 0 = 1fat n = n * fat (n -1)

Page 14: Unidade7 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.

Page 15: Unidade7 1

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

>, >=, ==, / =, <=, <

Page 16: Unidade7 1

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

Page 17: Unidade7 1

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

Page 18: Unidade7 1

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’ ?

Page 19: Unidade7 1

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’].

Page 20: Unidade7 1

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).

Page 21: Unidade7 1
Page 22: Unidade7 1

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

Page 23: Unidade7 1

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))

Page 24: Unidade7 1

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

Page 25: Unidade7 1

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)

Page 26: Unidade7 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

Page 27: Unidade7 1

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

Page 28: Unidade7 1

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

Page 29: Unidade7 1

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.

Page 30: Unidade7 1

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

Page 31: Unidade7 1

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

Page 32: Unidade7 1

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"

Page 33: Unidade7 1

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.

Page 34: Unidade7 1

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?

Page 35: Unidade7 1

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.

Page 36: Unidade7 1

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))?

Page 37: Unidade7 1

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.

Page 38: Unidade7 1

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?

Page 39: Unidade7 1

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.

Page 40: Unidade7 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.

Page 41: Unidade7 1

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)

Page 42: Unidade7 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).

Page 43: Unidade7 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)

Page 44: Unidade7 1

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).

Page 45: Unidade7 1

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.