Você está na página 1de 76

Andr Rauber Du Bois

Programao Funcional com a Linguagem Haskell

Andr Rauber Du Bois dubois@macs.hw.ac.uk

Andr Rauber Du Bois

ndice CAPTULO 1 Programao em Haskell __________________________________ 4 1.1 Expresses e Funes _________________________________________________ 4 1.2. Inteiros ____________________________________________________________ 6 1.3 Booleanos __________________________________________________________ 8 1.4 Caracteres e Strings___________________________________________________ 9 1.5 Nmeros em Ponto Flutuante __________________________________________ 11 1.6 Tuplas ____________________________________________________________ 12 1.7 Funes Recursivas __________________________________________________ 13 1.8 Exemplos _________________________________________________________ 15 CAPTULO 2 Listas em Haskell _______________________________________ 18 2.1 Listas _____________________________________________________________ 18 2.2 Operadores ________________________________________________________ 19 2.3 Funes sobre Listas _________________________________________________ 20 2.4 List Comprehensions ________________________________________________ 24 2.5 Definies _________________________________________________________ 26 2.6 Outras Funes teis sobre Listas ______________________________________ 30 2.7 Listas Infinitas______________________________________________________ 33 2.8 Erros _____________________________________________________________ 35 CAPTULO 3 Conceitos Avanados ____________________________________ 37 3.1 Currying __________________________________________________________ 37 3.2 Composio de Funes ______________________________________________ 39 3.3 Expresses Lambda _________________________________________________ 41 CAPTULO 4 Classes de Tipo _________________________________________ 43 4.1 Classes de Tipo _____________________________________________________ 43 4.2 Classes Derivadas ___________________________________________________ 45 4.3 Contexto __________________________________________________________ 46 4.4 Algumas Classes Importantes __________________________________________ 47 4.4.1 Enum ___________________________________________________________ 47 4.4.2 Read e Show _____________________________________________________ 48 4.4.3 Classes de Nmeros ________________________________________________ 48 2

Andr Rauber Du Bois


CAPTULO 5 Tipos Algbricos ________________________________________ 50 5.1 Tipos Algbricos ____________________________________________________ 50 5.2 Tipos Recursivos____________________________________________________ 52 5.3 Tipos Algbricos Polimrficos _________________________________________ 55 CAPTULO 6 Abstract Data Types _____________________________________ 57 6.1 Abstract Data Type (ADT) ____________________________________________ 57 6.2 Exemplo de ADT (Conjuntos) _________________________________________ 58 CAPTULO 7 IO ____________________________________________________ 68 7.1 Interao com o Usurio ______________________________________________ 68 7.2 Arquivos __________________________________________________________ 70 7.3 Interaes Infinitas __________________________________________________ 72 7.4 Mnadas __________________________________________________________ 73 CAPTULO 7 Construo de Mdulos __________________________________ 74 7.1 Mdulos __________________________________________________________ 74 7.2 Criando Um ADT ___________________________________________________ 76

Andr Rauber Du Bois


CAPTULO 1 Programao em Haskell

1.1 Expresses e Funes A idia principal da linguagem Haskell baseada na avaliao de expresses. A implementao da linguagem avalia (simplifica) a expresso passada pelo programador at sua forma normal. Por exemplo:

Haskell >Al Mundo!! Al Mundo!!

Neste exemplo foi passada para o interpretador Haskell a string (seqncia de caracteres) Al Mundo!!. O sistema respondeu com a mesma seqncia de caracteres, pois esta expresso no pode mais ser avaliada, j encontra-se normalizada. Pode-se utilizar comandos mais complexos:

Haskell> 4 + 3 7

ou

Haskell> ((9*6)+(59/3)) *27 1989.0

Um comando em Haskell uma frmula escrita na sintaxe da linguagem. Em Haskell existem vrias funes pr-definidas que podem ser usadas para a construo de expresses:

Haskell> reverse Al Mundo!! !!odnuM lA

Andr Rauber Du Bois

A funo reverse inverte a ordem dos caracteres em uma string. Apesar de existirem vrias funes pr-definidas, a grande idia da programao funcional que o usurio defina as suas prprias funes. As funes do usurio so definidas em scripts. Um script contm definies de funes associando nomes com valores e tipos. Em scripts da linguagem Haskell tambm existem comentrios que facilitam uma leitura posterior. Tudo o que for escrito depois de dois travesses (--) considerado comentrio e no interpretado. Segue um exemplo de script:

----exemplo.hs Neste script apresentam-se algumas definies simples

idade :: Int idade = 17

-- Um valor inteiro constante

maiorDeIdade :: Bool

-- Usa a definio de idade

maiorDeIdade = (idade>=18)

quadrado :: Int -> Int quadrado x = x * x

-- funo que eleva um nmero ao quadrado

mini :: Int -> Int -> Int mini a b | a <= b | otherwise =a =b

-- funo que mostra o menor valor entre dois inteiros

Andr Rauber Du Bois


A primeira linha de uma definio a declarao de tipo. A notao :: pode ser lida como possui tipo. Ento idade tem tipo Int, que em Haskell o tipo dos nmeros inteiros. A linha seguinte atribui o valor 17 para idade. Na definio seguinte introduzido o tipo Booleano, que pode ter dois valores, True ou False. No caso, maiorDeIdade tem o valor False pois 17 (valor de idade) menor do que 18. Na definio de maiorDeIdade foi utilizada a definio de idade. Em Haskell uma definio pode usar qualquer outra definio do mesmo script. Em scripts encontra-se tambm definies de funes. A funo quadrado no exemplo, uma funo que vai do tipo Int para o tipo Int. A funo atravs de seu argumento calcula uma resposta utilizando uma equao (x * x) que est no lado direito da definio. Por exemplo, se passarmos para o interpretador a funo quadrado e como argumento utilizarmos o valor 2 teremos:

Haskell> quadrado 2 4 A funo mini devolve o menor valor entre os seus dois argumentos, que so valores do tipo Int. Para se obter a resposta, testam-se os valores para se decidir qual o menor. Para isso so usados guards que so expresses booleanas iniciadas por uma barra |. No exemplo, se o valor de a menor ou igual que b a resposta a, seno passa-se para o prximo guard. Temos ento a expresso otherwise, que sempre possui a resposta se todos os outros guards falharem. Ex:

Haskell > mini 2 3 2 Outros detalhes sobre scripts, sero apresentados no decorrer do texto. 1.2. Inteiros O tipo Int o tipo dos nmeros inteiros em Haskell. Este tipo possui alguns operadores e funes:

Andr Rauber Du Bois


+, * ^ Soma e multiplicao de inteiros Potncia: 2^4 16 Serve para mudar o sinal de um inteiro ou para fazer a subtrao Tabela 1. Operadores do Tipo Int

div mod abs

Diviso de nmeros inteiros; div 10 3 3 O resto de uma diviso de inteiros; mod 10 3 1 Valor absoluto de um inteiro (remove o sinal).

negate Muda o sinal de um inteiro. Tabela 2. Funes do Tipo Int

Qualquer operador pode ser usado como funo, e qualquer funo pode ser usada como um operador, basta incluir o operador entre parnteses (), e a funo entre crases ``. Ex:

Haskell> (+) 2 3 5

Haskell> 10 `mod` 3 1 O programador pode definir os seus prprios operadores em scripts:

-- script do meu primeiro operador (&&&) :: Int -> Int -> Int a &&& b |a<b | otherwise

=a =b

Ex: Haskell> 10 &&& 3 3

Andr Rauber Du Bois

Pode-se trabalhar com ordenao e igualdade com os nmeros inteiros, assim como com todos os tipos bsicos. As funes de ordenao e igualdade tem como argumento dois nmeros inteiros e devolvem um valor do tipo Bool:

> >= == /= <= <

Maior que Maior ou igual Igual Diferente Menor ou igual Menor

Tabela 3. Ordenao e Igualdade

Ex:

Haskell> 29 > 15 True 1.3 Booleanos O tipo Bool o tipo dos valores booleanos True (Verdadeiro) ou False (Falso). Os operadores booleanos so:

&& || not

e ou negao

Tabela 4. Operadores Booleanos

Exemplo de definio utilizando Booleanos:

Andr Rauber Du Bois

-- ou exclusivo ouEx :: Bool -> Bool -> Bool ouEx x y = (x || y) && not (x && y)

O ou exclusivo poderia ser definido utilizando patterns ao invs de uma frmula:

ouEx True x ouEx False x

= not x =x

Este tipo de definio utiliza mais de uma equao. No exemplo, na primeira linha da definio, se for passado um valor True e um outro valor qualquer, a resposta ser a negao deste valor. Se no ocorrer este caso, passa-se para a segunda linha em que se passa como argumento um valor False e um outro valor qualquer, que ser a resposta.

1.4 Caracteres e Strings O tipo Char o tipo composto de caracteres, dgitos e caracteres especiais, como nova linha, tabulao, etc. Caracteres individuais so escritos entre aspas simples: a o caracter a e 7 o caracter sete. Alguns caracteres especiais so representados da seguinte maneira:

\t \n \ \ \\

Tabulao Nova linha Aspas simples () Aspas duplas () Barra (\)

Tabela 5. Caracteres Especiais

Andr Rauber Du Bois


Os caracteres so ordenados internamente pela tabela ASCII. Por isso:

Haskell> a < z True

Haskell> A< a True

Pode-se utilizar a barra para representar o caracter por seu nmero:

Haskell > \65 A

Existem funes que transformam um nmero em caracter, e um caracter em nmero inteiro, baseando-se na tabela ASCII. Respectivamente:

chr :: Int -> Char ord :: Char -> Int Listas de caracteres pertencem ao tipo String, e podem ser representados entre aspas duplas:

Al Mundo!! Haskell

Ex:

Haskell> Haskell \nLegal !! Haskell Legal

10

Andr Rauber Du Bois


Listas podem ser concatenadas usando o operador (++). Ex:

Haskell > Preciso de ++ \nfrases ++ melhores Preciso de frases melhores

A linguagem Haskell permite que se de sinnimos aos nomes de tipos. Exemplo:

type String = [Char]

Isto quer dizer que o tipo String um sinnimo de uma lista de caracteres. Ex:

Haskell> Haskell == [H, a, s, k, e, l, l] True

O assunto listas ser analisado mais profundamente no decorrer do texto.

1.5 Nmeros em Ponto Flutuante

Existe em Haskell o tipo Float, que trabalha com nmeros fracionrios que so representados em ponto flutuante. Pode-se escrever os nmeros com casas decimais ou utilizando notao cientfica; 231.6e-2 que significa 231.61 10-2, ou simplesmente 2.3161. O tipo Float alm de aceitar os operadores (+, - , *, ^, = =, /=, <=,>=, <, >) vistos anteriormente, possui algumas funes prprias:

11

Andr Rauber Du Bois


/ ** Cos, sin, tan log logBase Float -> Float -> Float Float -> Float -> Float Float -> Float Float -> Float Float -> Float -> Float Diviso Exponenciao, x ** x = xy Coseno, seno e tangente Logaritmo base e Logaritmo em qualquer base (primeiro argumento a base) read String -> Float Converte uma string representando um real, em seu valor show sqrt fromInt pi Float -> String Float -> Float Int -> Float Float Converte um nmero para uma string Raiz quadrada Converte um Int para um Float Constante Pi

Tabela 6. Funes do tipo Float

1.6 Tuplas Uma tupla em Haskell uma agregao de um ou mais componentes. Estes componentes podem ser de tipos diferentes. As tuplas so representadas em scripts por listas de componentes separados por vrgula, entre parnteses. O tipo de uma tupla parece uma tupla, mas possui tipos como componentes. Ex:

-- script com tuplas Type Nome = String Type Idade = Int -- Sinnimo para String (Nome) -- Sinnimo para Int (Idade)

verIdade :: (Nome, Idade) -> Idade verIdade (a,b) = b

-- Funo que se passa uma tupla -- (Nome, Idade), e devolve a idade

12

Andr Rauber Du Bois

Ento:

Haskell > verIdade (Andr, 21) 21

1.7 Funes Recursivas

Uma funo recursiva uma funo que chama a ela mesma. Grande parte das definies em Haskell sero recursivas, principalmente as que necessitam de algum tipo de repetio. Uma definio recursiva clssica a do fatorial de um nmero inteiro positivo: O fatorial de um nmero inteiro positivo pode ser dividido em dois casos: O fatorial de 0 ser sempre 1; E o fatorial de um nmero n>0, ser 1 * 2 *...* (n-1) * n

Ento:

fatorial :: Int -> Int fatorial 0 fatorial n =1 = n * fatorial (n-1) (regra 1) (regra 2)

Exemplo de avaliao:

fatorial 3 = 3 * (fatorial 2) = 3 * 2 * (fatorial 1) = 3 * 2 * 1 * (fatorial 0) =3*2*1*1 =6 (1) Multiplicao (2) (2) (2)

13

Andr Rauber Du Bois


Introduz-se agora um exemplo mais prtico de definio recursiva. Seja a funo aluno :: Int -> Float, que possui como argumento o nmero da chamada de um aluno (que pode variar de 1 at n), e fornece a nota do aluno na ltima prova como resultado.

Como se calcularia a mdia de notas da turma?

Para se resolver este problema, o ideal dividi-lo em partes menores. Poderamos primeiro pensar em uma funo soma :: Int -> Float, que soma a nota de todos os alunos. Esta funo teria dois casos: soma 1 seria a nota do aluno 1, ou simplesmente (aluno 1); soma n seria aluno 1 + aluno 2 + ... + aluno (n-1) + aluno n

Tem-se ento:

soma :: Int -> Float soma 1 soma n = aluno 1 = aluno n + soma (n-1)

Definida a funo soma, pode-se definir a funo mdia de maneira simples:

media :: Int -> Float media n = (soma n) / (fromInt n)

Na segunda linha da definio tem-se que usar a funo fromInt para transformar o valor n que tem tipo Int, em Float, pois o tipo do operador de diviso (/) :: Float -> Float -> Float.

14

Andr Rauber Du Bois


1.8 Exemplos

Nesta parte do texto analisa-se um exemplo mais extenso, usando as funes aluno e media explicadas anteriormente. O objetivo criar uma funo que gere uma tabela mostrando o nmero de todos os alunos e suas respectivas notas. No final da tabela deve aparecer a mdia das notas. Exemplo:

Haskell > tabela 4

Aluno 1 2 3 4

Nota 7.5 10 9 6.3

Mdia da Turma: 8.2

Podese resolver o problema utilizando uma abordagem top-down. A tabela pode ser pensada como sendo uma grande string. Ento

tabela :: Int -> String A funo tabela tem como argumento um nmero inteiro (nmero de alunos), e devolve uma string (a tabela). A definio dessa funo seria:

tabela n = cabealho ++ imprimeAlunos n ++ imprimeMedia n A funo cabealho tem uma definio direta:

cabealho :: String cabealho = Aluno Nota\n

15

Andr Rauber Du Bois

Para se imprimir as notas, devese imprimir um aluno por linha. Isto pode ser definido recursivamente utilizando uma outra funo

imprimeAluno :: Int -> String

Dessa maneira teremos:

imprimeAlunos :: Int -> String

imprimeAlunos 1 imprimeAlunos n

= imprimeAluno 1 = imprimeAlunos (n-1) ++ imprimeAluno n

Para a definio das funes imprimeAluno e imprimeMedia necessrio o uso da funo pr-definida show, que transforma um nmero de qualquer tipo em string:

imprimeAluno :: Int -> String imprimeAluno n = show n ++ ++ show (aluno n) ++ \n

imprimeMedia :: Int -> String imprimeMedia n = \n ++ Mdia da Turma: ++ show (media n) Foram usadas as funes aluno e media definidas anteriormente. Agora apresenta-se o script completo para a funo tabela: -- script tabela -- banco de dados das notas: aluno :: Int -> Float aluno 1 = 7.5 aluno 2 = 10 aluno 3 = 9 aluno 4 = 6.3 -- (...)

16

Andr Rauber Du Bois


tabela :: Int -> String tabela n = cabealho ++ imprimeAlunos n ++ imprimeMedia n

cabealho :: String cabealho = Aluno Nota\n

imprimeAlunos :: Int -> String imprimeAlunos 1 imprimeAlunos n = imprimeAluno 1 = imprimeAlunos (n-1) ++ imprimeAluno n

imprimeAluno :: Int -> String imprimeAluno n = show n ++ ++ show (aluno n) ++ \n

imprimeMedia :: Int -> String imprimeMedia n = \n ++ Mdia da Turma: ++ show (media n)

soma :: Int -> Float soma 1 soma n = aluno 1 = aluno n + soma (n-1)

media :: Int -> Float media n = (soma n) / (fromInt n)

A ordem em que as definies aparecem em um script no importante. importante ressaltar que os nomes das funes sempre comeam com letras minsculas, e os tipos com letras maisculas.

17

Andr Rauber Du Bois

CAPTULO 2 Listas em Haskell 2.1 Listas Em Haskell pode-se trabalhar com listas de vrios tipos diferentes. Para qualquer tipo t, pode-se criar uma lista com elementos do tipo t, que ser do tipo [t]. Exemplo:

[1, 2, 3, 4] [H, a, s, k, e, l, l] [False, True, True]

:: [Int] :: [Char] :: [Bool]

Pode-se trabalhar tambm com listas de listas, listas de tuplas e listas de funes (desde que as funes tenham o mesmo tipo):

[[1,2,3], [2,3], [3,4,5,6]] [(1,a), (2, b) , (3, c)] [(/), (**), logBase]

:: [[Int]] :: [(Int, Char)] :: [Float -> Float -> Float]

Um outro caso so as listas vazias, [], que no possuem elementos e podem ser de qualquer tipo:

[] [] []

:: [Bool] :: [Float] :: [Int -> Int]

A ordem e o nmero de ocorrncia dos elementos significante. Uma lista [3,4] diferente de uma lista [4,3], e uma lista [1] diferente de uma lista [1,1]. Existem outras maneiras de descrever listas: [a .. b] a lista [a, a+1, ..., b]. Ex:

18

Andr Rauber Du Bois


Haskell > [1 .. 6] [1, 2, 3, 4, 5, 6]

Haskell > [4 .. 2] []

[a, b .. c] a lista de elementos de a at c passo b a. Ex:

Haskell > [2,4 .. 10] [2, 4, 6, 8, 10]

Haskell > [1,3 .. 10] [1, 3, 5, 7, 9]

O ltimo elemento da lista o maior da seqncia e deve ser menor ou igual a c.

2.2 Operadores O operador (:) o operador de construo de listas. Toda a lista construda atravs deste operador, de elementos e de uma lista.

[1] [1, 2, 3, 4]

= 1 : [] = 1 : 2 : 3 : 4 : []

Este operador serve para todo o tipo de listas:

(:) :: Int -> [Int] -> [Int] (:) :: Char -> [Char] -> [Char] (:) :: Bool -> [Bool] -> [Bool] (...)

19

Andr Rauber Du Bois


O que se observa que este operador trabalha com um elemento e uma lista que devem ser do mesmo tipo. Na verdade este um operador polimrfico e seu tipo :

(:) :: t -> [t] -> [t] Onde t uma varivel de tipo que pode ser substituda por qualquer tipo (Int, Char, etc...). O conceito de polimorfismo ser esclarecido em maior profundidade no decorrer do texto. Outro operador para listas o de concatenao (++):

Haskell> [1, 2] ++ [3, 4] ++ [5, 6] [1, 2, 3, 4, 5, 6]

Apenas listas de mesmo tipo podem ser concatenadas, por isso:

(++) :: [t] -> [t] -> [t]

Aqui se usa a letra t como varivel de tipo. Porm pode-se usar qualquer letra minscula.

2.3 Funes sobre Listas

Na maioria das definies sobre listas ir se usar a recurso para se percorrer todos os elementos. Uma funo simples seria a funo para somar todos os elementos de uma lista de nmeros inteiros:

somaLista :: [Int] -> Int

Para esta funo existem dois casos: Caso Bsico: Somar os elementos de uma lista vazia [] que ir resultar em 0, e

20

Andr Rauber Du Bois


Passo Indutivo: Somar os elementos de uma lista no vazia. Em uma lista no vazia existe sempre o elemento head (o primeiro elemento), e o tail da lista, que a lista que sobra sem o elemento head. Por exemplo, a lista [1, 2, 3] tem head 1 e tail [2,3]. Uma lista com head a e tail x escrita (a:x). Ento a soma dos elementos de uma lista no vazia (a:x) dada somando a soma dos elementos de x.

A definio da funo seria:

somaLista [] somaLista (a:x)

=0 = a + somaLista x

(1) (2)

Ex:

Haskell> somaLista [1, 2, 3 ,4 ,5] 15

O comando avaliado da seguinte maneira:

somaLista [1, 2, 3, 4, 5] = 1 + somaLista [2, 3, 4, 5] = 1 + ( 2 + somaLista [3, 4, 5]) = 1 + (2 + ( 3 + somaLista [4, 5])) = 1 + (2 + ( 3 + ( 4 + somaLista [5]))) = 1 + (2 + ( 3 + ( 4 + ( 5 + somaLista [])))) = 1 + (2 + ( 3 + ( 4 + ( 5 + 0)))) = 15 (2) (2) (2) (2) (2) (1) (+) Uma funo que teria uma definio muito parecida com a definio de somaLista, seria a funo para determinar a lista cujos elementos so o dobro dos elementos de uma lista:

21

Andr Rauber Du Bois

dobraLista :: [Int] -> [Int] dobraLista [] dobraLista (a:x) = [] = 2*a : dobraLista x

Quais so os dois casos desta funo? O caso bsico determinar a lista cujos elementos so o dobro dos elementos de uma lista vazia. A resposta seria []. O passo indutivo consiste em considerar uma lista no vazia. Como se faz isso? Calcula-se o dobro do head e coloca-se este elemento como o primeiro da lista cujos elementos so o dobro dos elementos do tail. Ex:

Haskell > dobraLista [1, 2, 3] [2, 4, 6]

As funes apresentadas at o momento trabalham apenas com listas de um tipo especfico. Porm existem funes polimrficas que trabalham com listas de qualquer tipo. Um exemplo seria a funo length, pr-definida da linguagem, que d o nmero de elementos de uma lista:

length :: [t] -> Int

length []

=0

length (a:x) = 1 + length x A lista vazia tem tamanho 0. A lista no vazia, possui sempre um elemento a mais que o seu tail. Esta definio serve para qualquer tipo de listas, tanto para nmeros quanto para caracteres, etc, por isso usa-se a varivel de tipo t na declarao da funo.

22

Andr Rauber Du Bois


Um exemplo interessante que envolve recurso o de uma funo de ordenao de uma lista. O objetivo do algoritmo utilizado inserir o primeiro elemento da lista a ser ordenada no tail da lista ordenado:

ordenacao :: [Int] -> [Int]

ordenacao [] ordenacao (a:x)

= [] = insere a (ordenacao x)

Utiliza-se para a funo ordenacao uma abordagem top-down. Define-se a funo ordenacao utilizando-se a funo

insere :: Int -> [Int] -> [Int].

Inserir um elemento em uma lista vazia simples:

insere e []

= [e]

Para se inserir um elemento no lugar certo em uma lista ordenada tem-se dois casos:

Se o elemento a ser inserido menor ou igual ao head da lista, coloca-se este elemento como o primeiro Caso contrrio, insere-se o elemento no tail da lista e o head concatenado na resposta:

insere e (a:x) | e <=a | otherwise = e:(a:x) = a : insere e x

23

Andr Rauber Du Bois


Ex:

Haskell > ordenacao [3, 1, 2] [1, 2, 3]

2.4 List Comprehensions A List Comprehension uma maneira de se descrever uma lista inspirada na notao de conjuntos. Por exemplo, se a lista list [1, 7, 3], pode-se duplicar o valor dos elementos desta lista da seguinte maneira:

[ 2 * a | a < - list ]

que ter valor:

[2, 14, 6]

ou

Haskell > [2* a | a<- [1, 7, 3]] [2, 14, 6]

Na list Comprehension o a <-list chamado de generator (gerador), pois ele gera os dados em que os resultados so construdos. Os geradores podem ser combinados com predicados (predicates) que so funes que devolvem valores booleanos (a->Bool). Ex:

Haskell > [ a | a<-list, even a] []

24

Andr Rauber Du Bois

No exemplo a funo even devolve o valor booleano True se o seu argumento for um nmero par. Ento esta list comprehention devolve apenas os valores pares da lista list. Nos geradores pode-se trabalhar com qualquer tipo de pattern. Ex:

somaPares :: [(Int, Int)] -> [Int] somaPares lista = [ a+b | (a,b) <- lista]

Ex:

Haskell > somaPares [(2,3), (3,7), (4,5)] [5, 10, 9]

Quando se trabalha com mais de um gerador, o primeiro valor da primeira lista gerado e mantido enquanto se avalia os valores da lista seguinte, Ex:

pares :: [t] -> [u] -> [(t,u)] pares n m = [(a,b) | a <- n , b <-m]

Ento: Haskell > pares [1,2,3] [4,5] [(1,4), (1,5), (2,4), (2,5), (3,4), (3,5)]

Por exemplo, utilizando list Comprehensions e um predicado, pode-se criar um filtro para strings:

remove :: Char -> [Char] -> [Char] remove carac str = [c | c <-str , c/= carac]

25

Andr Rauber Du Bois


Exemplo:

Haskell > remove Este exemplo remove os espaos em branco! Esteexemploremoveosespaosembranco!

Um exemplo mais prtico: Tendo uma lista de tuplas, em que cada tupla tem-se o nmero do aluno, o nome do aluno e sua nota:

baseDeDados :: [(Int, String, Float)] baseDeDados = [ (1, Andr, 10.0), (2, Carlos, 6.8), (3,Maurcio, 7.0)]

Pode-se transformar esta lista em uma lista de nomes de alunos:

nomes :: [(Int, String, Float)] -> [String]

nomes list = [pegaNome a | a <-list] where pegaNome (a,b,c) = b Na funo nomes, foi usada a funo pegaNome, que foi definida localmente atravs da palavra reservada where. Esta definio no serve para nenhuma outra funo no script. Ela s funciona na funo nomes. A funo nomes poderia ter sido definida de uma maneira mais simples:

nomes list = [b | (a,b,c) <-list]

2.5 Definies

A maioria das definies sobre listas se encaixam em trs casos: folding, que a colocao de um operador entre os elementos de uma lista, filtering, que significa filtrar 26

Andr Rauber Du Bois


alguns elementos e mapping que a aplicao de funes a todos os elementos da lista. Os outros casos so combinaes destes trs, ou recurses primitivas. Existem funes pr-definidas em Haskell que servem para resolver estes casos. So as funes foldr1, map, e filter. Estas funes so todas polimrficas, ou seja, servem para listas de qualquer tipo e so high order functions. As high order functions so funes que recebem outras funes como argumento.

foldr1

Esta funo coloca um operador entre os elementos de uma lista: foldr1 () [ x1, x2, ..., xn] = x1 x2 ... xn

A definio em Haskell :

foldr1 :: (t -> t -> t) -> [t] -> t

foldr1 f [a] foldr1 f (a:b:x)

=a = f a (foldr1 f (b:x))

A funo tem como argumento um operador (ou melhor, uma funo com dois argumentos), e uma lista. Ex:

Haskell > foldr1 (&&) [True, False, True] False

Haskell > foldr1 (++) [Concatenar ,uma ,lista ,de ,strings ,em ,uma ,s.] Concatenar uma lista de strings em uma s.

Haskell > foldr1 (+) [1,2,3] 6

27

Andr Rauber Du Bois

Existe a funo foldr que tem um argumento a mais, que seria o que deve devolver como resposta caso seja passada uma lista vazia como argumento:

foldr :: (a -> a -> a) -> a -> [a] -> a

Ex:

Haskell > foldr (*) 1 [] 1

map

A funo map aplica uma funo a todos os elementos de uma lista. Para se aplicar f em uma lista (a:x), o head ser f aplicado a, e o tail ser dado por mapear f na lista x.

A definio:

map :: (t -> u) -> [t] -> [u]

map f [] map f (a:x)

= [] = f a : map f x

Uma outra definio utilizando list comprehension seria:

map f list = [f a | a < -list]

Exemplo:

Haskell > map length [Haskell, Hugs, GHC] [7, 4, 3] 28

Andr Rauber Du Bois

Haskell > map (2*) [1, 2, 3] [2, 4, 6]

filter A funo filter filtra a lista atravs de um predicado ou propriedade. Um predicado

uma funo que tem tipo t -> Bool, como por exemplo:

par :: Int -> Bool par n = (n `mod` 2 == 0) A funo filter definida:

filter :: (t ->Bool) -> [t] -> [t]

filter p [] = [] filter p (a:x) |pa | otherwise = a: filter p x = filter p x

Exemplo:

Haskell > filter par [2, 4, 5, 6, 10, 11] [2, 4, 6, 10]

Uma definio alternativa atravs de list comprehension seria:

filter p x = [ a | a <- x, p a]

29

Andr Rauber Du Bois


Utilizando a baseDeDados definida anteriormente pode-se fazer uma funo que de os nomes dos alunos com nota maior que 7.

alunos :: [(Int, String, Float)] -> [String]

alunos base = map pegaNome (filter nota base) where nota (a,b,c) = c>7 pegaNome (a,b,c) = b

Ento: Haskell > alunos baseDeDados [Andr]

2.6 Outras Funes teis sobre Listas

Muitas funes de manipulao de listas necessitam tomar, ou retirar, alguns elementos de uma lista a partir do incio. Para isto, a linguagem Haskell possui as seguintes funes:

take :: Int -> [t] -> [t] drop :: Int -> [t] -> [t] A funo take n gera uma lista com os n primeiros elementos da lista parmetro:

take _ [] take 0 _

= [] = []

take n (a:x) = a : take (n-1) x

30

Andr Rauber Du Bois


Ento:

Haskell > take 3 [1, 2, 3, 4, 5, 6] [1, 2, 3]

Haskell > take 0 [2, 4, 6, 8, 10] []

A funo drop n gera uma lista sem os n primeiros elementos da lista parmetro, sua definio parecida com a da funo take:

drop 0 list drop _ [] drop n (a:x)

= list = [] = drop (n-1) x

Exemplo: Haskell > drop 3 [2, 4, 6, 8, 10] [8, 10]

Haskell > drop 10 [2, 4, 6, 8, 10] []

Outras funes interessantes, que seguem o mesmo princpio, so as funes takeWhile e dropWhile, que tem como parmetro, ao invs de um nmero, uma funo de tipo (t -> Bool).

Haskell > takeWhile par [2,4,5,7, 2] [2, 4]

Haskell > dropWhile par [2,4,5,7, 2] [5, 7, 2] Definio da funo takeWhile:

31

Andr Rauber Du Bois

takeWhile :: (t -> Bool) -> [t] -> [t]

takeWhile p [] takeWhile p (a:x) |pa | otherwise

= []

= a: takeWhile p x = []

A dropWhile definida de maneira semelhante. Outra funo muito utilizada a funo zip, que transforma duas listas em uma lista de tuplas.

zip (a:x) (b:y) = (a,b) : zip x y zip _ _ = []

Haskell > zip [1, 3, 5] [2, 4, 6] [(1,2), (3, 4), (5, 6)]

Haskell > zip [1, 3, 5, 7, 9, 11] [2, 4, 6] [(1,2), (3, 4), (5, 6)]

A lista gerada pela funo zip sempre ter o mesmo nmero de elementos da menor lista passada como argumento. Existe uma funo pr-definida da linguagem derivada da funo zip, a funo zipWith:

ZipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

Ela funciona da seguinte maneira:

ZipWith op [x1, x2, x3, ...] [y1, y2, y3, ...] =

[op x1 y1, op x2, y2, op x3 y3, ...]

32

Andr Rauber Du Bois

2.7 Listas Infinitas A linguagem Haskell assim como todas as linguagens funcionais puras, so chamadas de non-strict languages, elas trabalham com a lazy evaluation, ou seja, os argumentos de funes so avaliados somente quando necessrio. Se temos por exemplo uma funo

f (x) = 7 e passamos o seguinte argumento:

f((21 + 33) * 8) = 7

realmente necessrio perder-se tempo computacional avaliando-se a expresso (21+33)*8, se a avaliao da funo sempre gera o valor 7, independente do argumento? Em linguagens imperativas como C e Pascal, os argumentos sempre so avaliados antes de serem passados para as funes. A lazy evaluation nos permite trabalhar com estruturas infinitas. Estas estruturas necessitariam de um tempo infinito de processamento, mas na lazy evaluation apenas partes de uma estrutura de dados precisam ser avaliadas. Uma das principais estruturas infinitas utilizadas so as listas. Um exemplo simples de lista infinita seria

uns = 1 : uns

Se passarmos esta estrutura para um interpretador:

Haskell > uns [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ^C {Interrupted}

33

Andr Rauber Du Bois


ele nos avaliaria a lista at que uma tecla de interrupo fosse usada. Pode-se usar funes com listas infinitas:

somaOsDoisPrimeiros :: [Int] -> Int somaOsDoisPrimeiros (a:b:x) = a+b

Temos:

Haskell > somaOsDoisPrimeiros uns 2 A estrutura uns

no precisa ser gerada por completo para que a funo

somaOsDoisPrimeiros seja avaliada. Um exemplo interessante de lista infinita a gerada pela funo pr-definida iterate:

iterate :: (t -> t) -> t -> [t]

iterate f x = [ x ] ++ iterate f (f x)

Esta funo constri uma seqncia em que o prximo elemento o valor gerado aplicando-se uma funo ao elemento anterior. Ex:

Haskell > iterate (+1) 1 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ^C {Interrupted} Pode-se definir uma funo que pegue todos os valores at a posio n em uma iterao.

valorEmUmaIterao :: (v -> v) -> v -> v ->v

34

Andr Rauber Du Bois


valorEmUmaIterao func inic valor = take valor (iterate func ini)

Ento:

Haskell > valorEmUmaIterao (*2) 1 3 [1, 2, 4]

Existem outras maneiras de se descrever listas infinitas:

[3 .. ] = [3, 4, 5, 6 ...

[2, 4 .. ] = [2, 4, 6, 8 ...

Podese definir ento uma funo que ache todas as potncias de um nmero inteiro:

pot :: Int -> [Int]

pot n = [ n^y | y <- [0 ..] ]

Temse ento

Haskell > pot 2 [ 1, 2, 4, 8 ^C {Interrupted}

2.8 Erros

Se for passado para a funo take um valor negativo, ela ira devolver uma mensagem de erro:

35

Andr Rauber Du Bois

Haskell > take (-1) [1,2] Program error: negative argument.

Existe uma funo em Haskell chamada

error :: String -> a que pra a avaliao de uma expresso caso ocorra um valor no desejado (). A definio final da funo take seria:

take take 0 _ take _ []

:: Int -> [a] -> [a] = [] = [] = x : take (n-1) xs = error "negative argument"

take n (x:xs) | n>0 take _ _

36

Andr Rauber Du Bois

CAPTULO 3 Conceitos Avanados

3.1 Currying

Em Haskell uma funo de dois ou mais argumentos, pode aceit-los um de cada vez. Isto se chama currying. Por exemplo:

soma :: Int -> Int -> Int

soma x y

x+y

Esta funo pega dois nmeros inteiros como argumento e os soma:

Haskell > soma 2 8 10 Se aplicarmos a funo soma a apenas um argumento (soma 1), teremos uma funo que aplicada a um argumento b, incrementa este valor (1+b). Pode-se definir ento uma funo incrementa da seguinte maneira:

incrementa :: Int -> Int

incrementa = soma 1

Ex: Haskell > incremeta 4 5

37

Andr Rauber Du Bois


Observa-se ento que uma funo de dois ou mais argumentos pode ser aplicada parcialmente (partial aplication) formando como resultado funes:

soma soma 2 soma 2 3

:: Int -> Int -> Int :: Int -> Int :: Int

Podese fazer definies do tipo:

incrementaLista :: [Int] -> Int

incrementaLista = map (soma 1) Neste exemplo existem duas aplicaes parciais de funes. A funo soma 1 incrementa um nmero inteiro, e a funo map (soma 1), que tem como argumento uma lista, incrementa todos os valores de uma lista de inteiros. A aplicao parcial pode ser analisada no clculo lambda. Considere como exemplo, uma expresso: y. (x. x + y)

Aplicando-se um argumento: y. (x. x + y) 3

obtem-se uma funo com apenas um argumento: x. x + 3

Os operadores da linguagem podem tambm ser parcialmente aplicados, o que gera os operator sections.

38

Andr Rauber Du Bois

(+1) (1+) (<=100)

Funo que incrementa. Funo que incrementa. Funo que devolve um valor booleano, True se o argumento for menor ou igual a 100 , False caso contrrio.

(Haskell ++) Funo que concatena a string Haskell no incio de outra string (++ Haskell) Funo que concatena a string Haskell no final de uma string Tabela 7. Operator Sections

Sendo op um operador, x e y argumentos, a regra a seguinte:

(op x) y (x op) y

= y op x = x op y

A funo incrementaLista poderia ter sido definida da seguinte maneira:

incrementaLista = map (+1)

A prpria funo soma poderia ter sido definida simplesmente:

soma = (+)

3.2 Composio de Funes A composio de funes utilizada para aplicao de funes sobre funes. Isso proporciona uma maneira de dividir um problema em vrias partes menores. Por exemplo, atravs da funo remove, definida anteriormente, pode-se definir uma funo que remove pontuao (,.!) em uma string:

39

Andr Rauber Du Bois


removePontuacao :: String -> String

removePontuacao str = remove ! (remove . ( remove , str) ) )

Ento:

Haskell > removePontuacao Haskell. muito bom, para manipular strings !!! Haskell muito bom para manipular strings

Existe um operador de composio de funes em Haskell (.), que ajuda a evitar o uso de vrios parnteses nas definies. A definio de removePontuacao ficaria da seguinte maneira:

removePontuacao = remove ! . remove . . remove ,

O operador de composio funciona como esta equao:

f (g x) = (f . g) x

e seu tipo

(.) :: (u -> v) -> (t -> u) -> (t -> v) Um exemplo interessante a definio de iteracao, que tem como parmetro uma funo e o nmero de vezes que esta deve ser composta com ela mesma:

iteracao :: (a->a) -> Int -> (a->a) iteracao f 1 = f iteracao f n = iteracao f (n-1) . f

40

Andr Rauber Du Bois


Tem se :

Haskell> iteracao (+1) 5 1 6

3.3 Expresses Lambda

Ao invs de usar equaes para definir funes, pode-se utilizar uma notao lambda, em que a funo no precisa ter um nome. Por exemplo a funo

sucessor :: Int -> Int

sucessor x = x+1

poderia ser definida como x. x+1 na notao lambda, ou

\x -> x + 1

em Haskell. Temos ento

Haskell > (\x -> x + 1) 10 11 Da mesma maneira a funo soma poderia ter sido definida da seguinte maneira:

soma = \ x y -> x + y

O operador de composio de funes definido utilizando a sintaxe lambda:

41

Andr Rauber Du Bois

(.)

:: (u -> v) -> (t -> u) -> (t -> v)

f.g

= \x -> f (g x)

42

Andr Rauber Du Bois


CAPTULO 4 Classes de Tipo

4.1 Classes de Tipo

Observando o operador (==), nota-se que ele tem tipo:

(==) :: t -> t-> Bool

ou seja, ele uma funo polimrfica. Porm este polimorfismo diferente do da funo length. Analisando-se a definio da funo length, observa-se que a mesma definio vale para qualquer tipo de listas:

length :: [t] -> Int

length []

=0

length (a:x) = 1 + length x

J o operador (==) tem uma definio diferente para cada tipo, pois no o mesmo algoritmo que calcula a igualdade entre listas, caracteres ou nmeros. Este operador tambm no funciona para todos os tipos, por exemplo, no existe um algoritmo que diga se uma funo igual a outra. Em Haskell chama-se classe o conjunto de tipos sobre os quais uma funo definida. Por exemplo a equality class, ou classe Eq, o conjunto de tipos em que o operador (==) definido. A classe Eq definida da seguinte maneira:

class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x==y)

43

Andr Rauber Du Bois

Na verdade ela possui os operadores de igualdade e de diferena. Definindo-se um tipo Endereco (a definio de tipos Algbricos ser explicada detalhadamente no prximo captulo):

data Endereco = Rua String Residencia

data Residencia = Casa Int | Apto Int Int

e avaliando-se no interpretador algo do tipo:

Haskell > Rua "Farofa" (Casa 3) == Rua "Farinha" (Casa 3)

a resposta ser um erro:

ERROR: Endereco is not an instance of class "Eq"

Pode-se definir ento uma funo que iguala endereos:

iguala :: Endereco -> Endereco -> Bool

iguala (Rua x (Casa y)) (Rua a (Casa b)) iguala (Rua x (Apto y z)) (Rua a (Apto b c)) iguala _ _ = False

= (x==a) && (y == b) = (x==a) && (y == b) && (z==c)

Exemplo:

Haskell > iguala (Rua "Abobora" (Apto 13 403)) (Rua "Abobora" (Apto 13 403)) True

44

Andr Rauber Du Bois


Com a funo iguala possvel instanciar o tipo Endereco na classe Eq da seguinte maneira:

instance Eq Endereco where (==) = iguala Depois de feita a instanciao possvel usar o operador (==) diretamente em valores do tipo Endereco:

Haskell > (Rua "Abobora" (Apto 13 403)) == (Rua "Abobora" (Apto 13 403)) True

Tambm pode-se usar o operador (/=), pois na classe ele definido como:

x /= y

= not (x==y)

Ento:

Haskell > (Rua "Azul" (Apto 1 403)) /= (Rua "Marrom" (Casa 10)) True 4.2 Classes Derivadas

Uma classe pode ser derivada de outra. Dessa maneira alm de ter as suas operaes prprias, possui tambm as operaes da super-classe. Um exemplo de classe derivada a classe Ord. Ela definida de uma maneira similar:

class (Eq a) => Ord a where

(<), (<=), (>=), (>) max, min

:: a -> a -> Bool :: a -> a -> a

45

Andr Rauber Du Bois

Para um tipo pertencer a esta classe, ele tambm tem que pertencer a classe Eq. Pode-se dizer tambm que a classe Ord herda as operaes da classe Eq, o que tornaria a idia mais parecida com a da orientao a objetos. Haskell tambm permite heranas mltiplas, pois uma classe pode ter mais de uma super-classe:

Class (Eq t, Show t) => Minhaclasse t where ...

4.3 Contexto

Considere a definio da funo elem, que devolve um valor booleano dizendo se um elemento pertence a uma lista:

elem x []

= False

elem x (y:ys) = x == y || (elem x ys)

Como foi visto at agora, o tipo desta funo poderia ser polimrfico:

elem :: a -> [a] -> Bool

Mas analisando-se a definio da funo, nota-se que ela s funciona se o tipo a puder ser igualado com o operador (==), pois toda a definio se baseia neste operador. Como o tipo a tem que estar instanciado na classe Eq, uma melhor definio de tipo para a funo elem seria:

elem :: (Eq a) => a -> [a] -> Bool Esta definio pode ser lida como Para cada tipo a que pertencer a classe Eq, a funo elem tem tipo a -> [a] -> Bool. 46

Andr Rauber Du Bois


A expresso Eq a chamada de contexto da funo e no faz parte da expresso de tipo. O contexto de uma funo pode ser omitido, porm uma boa prtica de programao utiliza-lo, pois facilita a compreenso da funo.

4.4 Algumas Classes Importantes

Alm das classes Ord e Eq citadas anteriormente existem vrias outras classes prdefinidas em Haskell. Aqui sero mostradas algumas das mais importantes:

4.4.1 Enum

a classe dos tipos que podem ser enumerados, podendo ento gerar listas como

Haskell > [2,4, .. 8] [2, 4, 6, 8]

A lista [2,4, ..8] descrita na funo enumFromThenTo 2 4 8. Pode-se gerar listas desta maneira em qualquer tipo instanciado na classe Enum, como por exemplo Char, ou qualquer outro Tipo Algbrico definido pelo programador. As principais funes da classe Enum so:

class (Ord a) => Enum a where enumFrom enumFromThen enumFromTo enumFromThenTo :: a -> [a] :: a -> a -> [a] :: a -> a -> [a] -- [n..] -- [n,m..] -- [n..m]

:: a -> a -> a -> [a] -- [n,n'..m]

47

Andr Rauber Du Bois

4.4.2 Read e Show

Os tipos instanciados na classe Show, so todos os tipos que podem ser convertidos para listas de caracteres (strings). A classe Read fornece operaes para transformar strings em valores de algum tipo. As principais funes so:

show :: (Show t)

=> t -> String

read :: (Read t)

=> String -> t

4.4.3 Classes de Nmeros A classe mais geral dos nmeros a classe Num. Todos os nmeros possuem algumas operaes em comum, so elas:

class (Eq a, Show a, Eval a) => Num a where (+), (-), (*) negate abs, signum fromInteger fromInt :: a -> a -> a :: a -> a :: a -> a :: Integer -> a :: Int -> a

Outras operaes numricas so restritas a subclasses. Por exemplo, div e mod so operaes da classe Integral, o que quer dizer que somente valem para os tipos Integer e Int, que a princpio so os nicos tipos instanciados nesta classe. Outro exemplo o operador de diviso (/) que s vale para os tipos da classe Fractional.

48

Andr Rauber Du Bois


Uma boa maneira para aprender classes (e Haskell em geral) consultar o script Preludio.hs que vem junto com as distribuies da linguagem. L encontra-se a definio de todas as classes, alm de vrias funes primitivas.

49

Andr Rauber Du Bois


CAPTULO 5 Tipos Algbricos

5.1 Tipos Algbricos

At agora foram apresentados vrios tipos intrnsecos da linguagem, como valores booleanos, caracteres e nmeros. Porm existem certos problemas computacionais que so mais difceis de serem modelados com estes valores, como por exemplo os meses. Pode-se definir um tipo Meses da seguinte maneira:

data Meses = Jan | Fev | Mar | Abr | Mai | Jun | Jul | Ago | Set | Out | Nov | Dez

Este tipo um enumerated type. Ele formado por doze valores que so chamados de construtores (constructors) de tipo. Uma definio de tipo comea sempre com a palavra data, depois vem o nome do tipo (Meses), que deve comear com letra maiscula (note que todos os tipos em Haskell comeam com letra maiscula), e seus construtores (que tambm comeam com letra maiscula). Os construtores so todos os valores que um tipo pode assumir. Um exemplo de tipo algbrico j conhecido o tipo Bool:

data Bool = True | False

Pode-se criar um tipo que possua vrios componentes:

type Nome = String type Idade = Int

data Pessoas = Pessoa Nome Idade

As funes que manipulam os tipos algbricos podem ser definidas por pattern matching:

50

Andr Rauber Du Bois

mostraPessoa :: Pessoas -> String

mostraPessoa (Pessoa nom idade) = Nome: ++ nom ++ Idade: ++ show idade

Exemplo:

Haskell > mostraPessoa (Pessoa derson Arajo 22) Nome: derson Arajo Idade: 22

Um tipo pode trabalhar com valores bem diferentes. Supondo que se queira trabalhar com figuras geomtricas. O tipo pode assumir o valor de um crculo ou de um retngulo. Ento:

data Forma = Circulo Float | Retangulo Float Float

O valor para o crculo poderia ser o raio, e para o retngulo poderia ser base e altura. Para se definir uma funo que calcula a rea de um objeto do tipo Forma pode-se trabalhar novamente com pattern matching:

area :: Forma -> Float

area (Circulo r) area (Retangulo b a)

= pi * r * r =b*a

Outro exemplo do mesmo princpio seria para se trabalhar com endereos. Algumas ruas tem nomes e outras so representadas por nmeros. Ex:

data Rua = Numero Int Residencia | Nome String Residencia

51

Andr Rauber Du Bois

data Residencia = Casa Int | Apartamento Int Int

Agora pode-se definir operaes que formatam o endereo usando pattern matching em cima dos construtores.

Quando um tipo definido, algumas classes podem se instanciadas diretamente atravs da palavra reservada deriving:

data Meses = Jan | Fev | Mar | Abr | Mai | Jun | Jul | Ago | Set | Out | Nov | Dez deriving (Eq, Show, Enum)

Desta maneira, pode-se fazer coisas do tipo:

Haskell > Jan Jan

Haskell > Mar == Mar True

Haskell > [Jan .. Set] [Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set]

5.2 Tipos Recursivos

Os tipos algbricos podem ser tambm recursivos. Um exemplo tpico o de construo de rvores:

data Arvore = Null | Node Int Arvore Arvore

52

Andr Rauber Du Bois

Uma rvore pode ser um valor nulo ou um node que composto por um valor inteiro e duas sub-rvores. Ex: Node 22 Null Null

22

Figura 3. rvore (1)

Node 12 (Node 1 Null Null) (Node 15 (Node 16 Null Null) Null)

12

15

16 1

Figura 4. rvore (2)

Pode-se fazer definies recursivas em cima de rvores, como por exemplo uma funo que some todos os elementos:

somaArvore :: Arvore -> Int

somaArvore Null

=0

53

Andr Rauber Du Bois


somaArvore (Node valor esq dir) = valor + somaArvore (esq) + somaArvore (dir)

ou uma funo que diz se um valor est na rvore ou no:

procuraValor :: Arvore -> Int ->Bool procuraValor Null num = False procuraValor (Node valor esq dir) num | valor == num = True | otherwise = False || (procuraValor esq num) || (procuraValor dir num)

Uma funo que segue o mesmo princpio a funo ocorrencia que diz quantas vezes um valor aparece na rvore:

Para uma rvore sem valores a resposta 0. Se o valor do Node for igual ao valor procurado a resposta 1 somado com o nmero de ocorrncia do valor na rvore da esquerda, somada com a ocorrncia na rvore da direita. Seno a resposta a soma das ocorrncias direita com as da esquerda.

Tem-se ento a seguinte definio:

ocorrencia :: Arvore -> Int -> Int

ocorrencia Null num

=0

ocorrencia (Node valor esq dir) num | valor == num | otherwise = 1 + ocorrencia esq num + ocorrencia dir num = ocorrencia esq num + ocorrencia dir num

54

Andr Rauber Du Bois


5.3 Tipos Algbricos Polimrficos

Os tipos Algbricos podem ser tipos com definies polimrficas. Um exemplo simples, mas ilustrativo, seria a definio de um tipo Pares. Um par poderia ser tanto dois nmeros, quanto dois caracteres ou dois valores booleanos. Temos:

data Pares u = Par u u

Um par seria

par1 :: Pares Int par1 = Par 22

ou de qualquer outro tipo:

Par [1,2] [2,3,4] Par False True

:: Pares [Int] :: Pares Bool

(...)

A definio anterior dada para rvores trabalhava com nmeros inteiros nos nodes. Pode-se modificar a definio para que a rvore contenha qualquer tipo de valor nos nodes. Ento o tipo rvore seria:

data Arvore t = Null | Node t (Arvore t) (Arvore t) As definies de procuraValor e ocorrencia, precisariam ser modificadas apenas na tipagem:

55

Andr Rauber Du Bois


procuraValor :: Arvore t -> Int ->Bool

ocorrencia :: Arvore t -> Int -> Int

J a funo somaArvore s funciona para rvores de nmeros inteiros. Por isso o tipo passa a ser:

somaArvore :: Arvore Int -> Int

56

Andr Rauber Du Bois


CAPTULO 6 Abstract Data Types

6.1 Abstract Data Type (ADT) Supondo que se queira modelar uma base de dados para uma loja de bebidas. Poderia-se ter a seguinte definio.

type Codigo = Int

type Produto = String

type Preco = Float

type Base = [(Codigo, Produto, Preco)]

base1 :: Base

base1 = [ (1, Guarana, 0.70), (2, Cerveja Bacana lata, 0.50), (3, Usque Do Bom, 22.0) ......]

Teria-se ento algumas definies em cima desta base. insereProduto :: Base -> (Codigo, Produto, Preco) -> Base

retiraProduto :: Base -> Codigo -> Base

preco :: Base -> Codigo -> Preco

Se a base estiver ordenada pelo cdigo do produto, fica muito mais fcil de se implementar as funes, pois todas fazem algum tipo de procura em cima da base. O nico

57

Andr Rauber Du Bois


problema que pode-se inserir um novo cadastro na base simplesmente usando o operador (:), pois a base uma lista:

Haskell > (33, Cachaa Maldio, 0.55) : base1 [(33, Cachaa Maldio, 0.55), (1, Guarana, 0.70), (2, Cerveja Bacana lata, 0.50), (...)

Isto tornaria a base desordenada, e as funes definidas anteriormente no funcionariam mais. Para se modelar a loja de bebidas seria necessrio criar um tipo que tivesse apenas as operaes insereProduto, retiraProduto e preco. Quando permitimos que um tipo funcione apenas para um certo conjunto de operaes, chamamos este tipo de abstract data type (ADT).

6.2 Exemplo de ADT (Conjuntos)

Poderia-se implementar um conjunto como sendo uma lista de valores ordenados sem repetio. Mas para a lista manter estas qualidades seria necessrio criar um ADT, para que o conjunto s fosse manipulado pelas funes a ele pertencentes. A declarao de um ADT feita da seguinte maneira:

type Conjunto t = [t] in vazio unitario membroConj uniao inter dif :: Conjunto t, :: t -> Conjunto t, :: Ord t => Conjunto t -> t -> Bool, :: Ord t => Conjunto t -> Conjunto t -> Conjunto t, :: Ord t => Conjunto t -> Conjunto t -> Conjunto t, :: Ord t => Conjunto t -> Conjunto t -> Conjunto t,

58

Andr Rauber Du Bois


conjIgual subConj leqConj constConj mapConj filterConj foldConj mostra card conjUniao conjInter :: Eq t => Conjunto t -> Conjunto t -> Bool, :: Ord t => Conjunto t -> Conjunto t -> Bool, :: Ord t => Conjunto t -> Conjunto t -> Bool, :: Ord t => [t] -> Conjunto t, :: Ord u => (t->u) -> Conjunto t -> Conjunto u, :: (t->Bool) -> Conjunto t -> Conjunto t, :: (t->t->t) -> t -> Conjunto t -> t, :: Show t => Conjunto t -> ShowS, :: Conjunto t -> Int, :: Ord t => Conjunto (Conjunto t) -> Conjunto t, :: Ord t => Conjunto (Conjunto t) -> Conjunto t

Depois seguem as definies das funes. As funes vazio e unitario so de simples definio:

vazio = []

unitario a = [a] A funo membroConj devolve um valor booleano que diz se o elemento passado como parmetro est ou no no conjunto. Esta definio recursiva, e utiliza a caracterstica do conjunto ter seus elementos em ordem:

membroConj [] a = False membroConj (a:x) b | a<b | a == b | otherwise = membroConj x b = True = False

As funes uniao, inter e dif, que fazem respectivamente unio, interseco e diferena de dois conjuntos, possuem o mesmo tipo e definies semelhantes:

59

Andr Rauber Du Bois

uniao [] a uniao a []

=a =a

uniao (a:x) (b:y) | a<b | a == b | otherwise = a : uniao x (b:y) = a : uniao x y = b : uniao (a:x) y

inter [] a inter a []

= [] = []

inter (a:x) (b:y) | a<b | a == b | otherwise = inter x (b:y) = a : inter x y = inter (a:x) y

dif [] a = [] dif a [] = a dif (a:x) (b:y) | a == b |a<b | otherwise = dif x y = a : dif x (b:y) = dif (a:x) y

A subConj (devolve um valor booleano dizendo se um conjunto ou no subconjunto de outro) definida como as outras por pattern matching tendo trs casos: Um conjunto vazio sub-conjunto de outro Um conjunto no-vazio no sub-conjunto de um conjunto vazio O terceiro caso quando nenhum dos conjuntos passados como parmetro so vazios. Ento so feitas chamadas recursivas da funo aproveitando o fato dos elementos estarem em ordem.

60

Andr Rauber Du Bois

subConj [] a = True subConj x [] = False subConj (a:x) (b:y) | a<b | a == b | a>b = False = subConj x y = subConj (a:x) y

A funo constConj transforma uma lista em um conjunto. Para isso se utiliza a funo sort para ordenao dos elementos (funo explicada anteriormente), e uma funo eliminaRep, que retira os elementos repetidos da lista:

constConj = eliminaRep . sort

sort :: Ord t => [t] -> [t]

sort [] sort (a:x)

= [] = ins a (sort x)

ins :: Ord t => t -> [t] -> [t]

ins a []= [a] ins a (b:y) | a <= b | otherwise = a : (b:y) = b : ins a y

61

Andr Rauber Du Bois


eliminaRep :: (Ord t) => [t] -> [t]

eliminaRep [] = [] eliminaRep [a] eliminaRep (a:b:x) | a == b | otherwise = eliminaRep (b:x) = a : eliminaRep (b:x) = [a]

Exemplos:

Haskell > constConj [5, 7, 4, 3, 89, 23, 2, 3, 3, 7, 4] [2, 3, 4, 5, 7, 23, 89]

Haskell > uniao (constConj [4, 3, 1, 22, 4]) (constConj [4, 34, 1, 3]) [1, 3, 4, 22, 34]

Haskell > inter (uniao (constConj [1,2,3]) (constConj [2,3,4,5])) (constConj [3,4,5]) [3, 4, 5]

Algumas definies so feitas simplesmente igualando-se uma funo a outra:

conjIgual = (==)

leqConj = (<=)

filterConj = filter

62

Andr Rauber Du Bois


foldConj = foldr

card = length

Na verso de map para conjuntos deve-se cuidar os elementos repetidos que podem aparecer:

mapConj f l = eliminaRep (map f l)

Pode-se definir as funes conjUniao e conjInter que fazem a unio e interseco de conjuntos de conjuntos, utilizando as funes foldConj, uniao e inter definidas anteriormente:

conjUniao = foldConj uniao []

conjInter = foldConj inter []

Para se poder trabalhar com os conjuntos, eles devem estar instanciados na classe show. Um conjunto pode ser exibido na tela da mesma maneira que uma lista. Por isso se usa a funo showList.

mostra = showList

instance Show t => Show (Conjunto t) where showsPrec p = mostra

Para se facilitar algumas operaes pode-se instanciar o tipo Conjunto em outras classes de tipos:

63

Andr Rauber Du Bois


instance Eq t => Eq (Conjunto t) where (==) = conjIgual

instance Ord t => Ord (Conjunto t) where (<=) = leqConj

Existem vrias outras definies que podem ser feitas sobre conjuntos. Para isso basta acrescentar a type signature da funo na lista de funes e depois a sua definio. Segue agora o script completo do tipo conjunto:

-- script de conjuntos type Conjunto t = [t] in vazio unitario membroConj uniao inter dif conjIgual subConj leqConj constConj mapConj filterConj foldConj mostra card conjUniao :: Conjunto t, :: t -> Conjunto t, :: Ord t => Conjunto t -> t -> Bool, :: Ord t => Conjunto t -> Conjunto t -> Conjunto t, :: Ord t => Conjunto t -> Conjunto t -> Conjunto t, :: Ord t => Conjunto t -> Conjunto t -> Conjunto t, :: Eq t => Conjunto t -> Conjunto t -> Bool, :: Ord t => Conjunto t -> Conjunto t -> Bool, :: Ord t => Conjunto t -> Conjunto t -> Bool, :: Ord t => [t] -> Conjunto t, :: Ord u => (t->u) -> Conjunto t -> Conjunto u, :: (t->Bool) -> Conjunto t -> Conjunto t, :: (t->t->t) -> t -> Conjunto t -> t, :: Show t => Conjunto t -> ShowS, :: Conjunto t -> Int, :: Ord t => Conjunto (Conjunto t) -> Conjunto t,

64

Andr Rauber Du Bois


conjInter vazio = [] :: Ord t => Conjunto (Conjunto t) -> Conjunto t

unitario a = [a]

membroConj [] a = False membroConj (a:x) b | a<b | a == b = membroConj x b = True

| otherwise = False

uniao [] a uniao a []

=a =a

uniao (a:x) (b:y) | a<b | a == b = a : uniao x (b:y) = a : uniao x y

| otherwise = b : uniao (a:x) y

inter [] a inter a []

= [] = []

inter (a:x) (b:y) | a<b | a == b = inter x (b:y) = a : inter x y

| otherwise = inter (a:x) y

dif [] a = [] dif a [] = a dif (a:x) (b:y) | a == b |a<b = dif x y = a : dif x (b:y)

| otherwise = dif (a:x) y

65

Andr Rauber Du Bois

conjIgual = (==)

subConj [] a = True subConj x [] = False subConj (a:x) (b:y) | a<b | a == b | a>b = False = subConj x y = subConj (a:x) y

leqConj = (<=)

constConj = eliminaRep . sort

sort :: Ord t => [t] -> [t] sort [] sort (a:x) = [] = ins a (sort x)

ins :: Ord t => t -> [t] -> [t] ins a []= [a] ins a (b:y) | a <= b = a : (b:y)

| otherwise = b : ins a y

eliminaRep :: (Ord t) => [t] -> [t] eliminaRep [] = [] eliminaRep [a] eliminaRep (a:b:x) | a == b = eliminaRep (b:x) = [a]

| otherwise = a : eliminaRep (b:x)

66

Andr Rauber Du Bois

mostra = showList

instance Eq t => Eq (Conjunto t) where (==) = conjIgual

instance Ord t => Ord (Conjunto t) where (<=) = leqConj

instance Show t => Show (Conjunto t) where showsPrec p = mostra

mapConj f l = eliminaRep (map f l)

filterConj = filter

foldConj = foldr

card = length

conjUniao = foldConj uniao []

conjInter = foldConj inter []

67

Andr Rauber Du Bois

CAPTULO 7 IO

7.1 Interao com o Usurio

Haskell, como as outras linguagens de programao, possui funes que se comunicam com o sistema operacional para realizar entrada e sada de dados. Estas operaes trabalham com valores do tipo (IO t), e durante a sua avaliao requisitam operaes de IO ao sistema operacional. Ex:

main = putStr Sada de dados!!!

sendo que

putStr :: String -> IO ().

Se o valor devolvido por uma funo for do tipo IO, o interpretador Haskell no responde simplesmente imprimindo o valor na tela, e sim mandando uma requisio ao sistema operacional para que faa a entrada ou sada de dados. Ex:

Haskell > main Sada de dados !!!

O tipo IO polimrfico. Se olharmos para o tipo da funo getChar

getChar :: IO Char

sabe-se que ela realiza uma ao e retorna um caracter. Quando uma funo no retorna nada de til utiliza-se o tipo ( ). Por exemplo, a funo

68

Andr Rauber Du Bois

putChar :: Char -> IO ( )

tem como argumento um caracter, e no devolve nenhum valor. o mesmo caso da funo putStr. Uma seqncia de entrada e sada de dados expressa atravs de uma expresso do. Segue um exemplo de programa simples utilizando o do.

main = do putStr (Escreva uma palavra: ) palavra <- getLine putStr (Palavra invertida: ++ reverse palavra) A funo getLine faz com que o sistema operacional leia uma linha, e associe esta seqncia de caracteres varivel a esquerda da flecha (<-), ou seja palavra. Esta varivel s pode ser acessada dentro da expresso do. Por isso a funo tem tipo:

getLine :: IO String.

O programa roda da seguinte maneira:

Haskell > main Escreva uma palavra: Haskell Palavra invertida: lleksaH

Haskell >

sendo que a palavra sublinhada foi a entrada do usurio. Note que a funo main possui o seguinte tipo:

main :: IO ()

69

Andr Rauber Du Bois

pois ela no devolve nenhum valor, simplesmente realiza uma srie de aes de entrada e sada.

7.2 Arquivos

Existem duas funes principais para se trabalhar com o sistema de arquivos:

writeFile :: String -> String -> IO ()

readFile :: String -> IO String

O funcionamento delas, pode ser facilmente demonstrado atravs de exemplos. Primeiro uma funo para criar um arquivo contendo uma string digitada pelo usurio:

main = do putStr ("Escreva uma linha e tecle ENTER: ") linha <- getLine nome <- criaArq linha putStr ("A linha \n" ++ linha ++ "\nesta no arquivo " ++ nome ++ "!")

criaArq :: String -> IO String criaArq linha = do putStr ("Nome do Arquivo a ser criado: ") nome <- getLine writeFile nome linha return (nome)

70

Andr Rauber Du Bois


A funo principal pega uma linha e a usa como parmetro para a funo criaArq. Esta solicita o nome do arquivo a ser criado e o cria com a funo writeFile, ento devolve o nome do arquivo com a funo

return :: a -> IO a.

A interao com o usurio ocorre da seguinte maneira:

Haskell > main Escreva uma linha e tecle ENTER: Trabalhando com Arquivos Nome do Arquivo a ser criado: Haskell.txt A linha Trabalhando com Arquivos esta no arquivo Haskell.txt!

Haskell > Pode-se fazer uma funo que crie o arquivo Haskell2.txt com o contedo de Haskell.txt mais uma linha digitada pelo usurio:

adiciona = do putStr ("Escreva uma linha para adicionar ao arquivo Haskell2.txt:\n") linha <- getLine arquivo <- readFile "Haskell.txt" writeFile "Haskell2.txt" (arquivo ++ "\n" ++ linha) putStr ("Linha adicionada!") A funo readFile associa Haskell.txt a varivel arquivo, isso feito by-need, ou seja o contedo s lido quando necessrio, seguindo a estratgia da lazy evaluation.

71

Andr Rauber Du Bois


7.3 Interaes Infinitas

Usando a recurso pode se trabalhar com entrada de dados ilimitada. Para ilustrar este conceito, segue um exemplo de funo que l vrios nomes e os exibe em ordem alfabtica:

main = do nomes <- leNomes putStr (unlines (sort nomes))

leNomes = do putStr ("Escreva um nome: ") nome <- getLine if nome == "" then return [] else do nomes <- leNomes return ([nome] ++ nomes)

sort [] = [] sort (a:b) = sort [x | x <- b, x<a] ++ [a] ++ sort [x | x <- b, x>= a]

A funo leNomes chamada recursivamente at que receba uma string vazia. O controle feito atravs da funo (if then else), que funciona como em outras linguagens de programao. leNomes devolve uma lista de strings, onde cada elemento um nome que foi digitado pelo usurio. Esta lista ordenada na funo principal pelo sort que utiliza um algoritmo diferente da funo ordenacao vista anteriormente. O resultado exibido depois de passar pela funo

72

Andr Rauber Du Bois

unlines :: [String] -> String

que recebe uma lista de strings, e a transforma em uma string colocando o caracter de nova linha (\n) entre os elementos. Um exemplo do uso da funo seria:

Haskell > main Escreva um nome: Joao Escreva um nome: Marcelo Escreva um nome: Andre Escreva um nome: Carlos Escreva um nome: Andre Carlos Joao Marcelo

Haskell >

7.4 Mnadas

Apesar do sistema de I/O da linguagem Haskell parecer com programao imperativa, puramente funcional. Ele baseado na teoria das Mnadas. Mesmo assim, no necessrio se compreender a teoria das Mnadas para se programar utilizando I/O. Os operadores de Mnadas utilizados para construir o sistema de entrada e sada da linguagem Haskell tambm podem ser usados para outros propsitos de programao. Mais sobre Mnadas pode ser visto em [THO 96].

73

Andr Rauber Du Bois

CAPTULO 7 Construo de Mdulos

7.1 Mdulos

Pode-se pensar na implementao da estrutura de dados pilha, como sendo uma lista:

data Pilha t = Stack [t] deriving (Eq,Show)

e ento definir-se algumas operaes bsicas sobre ela. A funo push coloca um elemento no topo da pilha:

push :: t -> Pilha t -> Pilha t

push x (Stack y)

= Stack (x:y)

a funo pop retira o elemento do topo da pilha:

pop :: Pilha t -> t

pop (Stack []) pop (Stack (a:b))

= error Pilha vazia!! =a

pilhaVazia :: Pilha t

pilhaVazia = Stack []

74

Andr Rauber Du Bois


Exemplos:

Haskell > push 1 pilhaVazia Stack [1]

Haskell > pop (Stack [4,5,6]) 4

Esta implementao de pilha pode ser reutilizada por outros programas em Haskell. Para isso necessrio criar um mdulo. O mdulo Pilha seria construdo da seguinte maneira:

module Pilha ( Pilha (Stack), pilhaVazia, push, pop) where (...)

Para se criar um mdulo, utiliza-se a palavra reservada module, e em seguida o nome do mdulo. Aps o nome, lista-se todas as funes que se quer utilizar em outros programas. Logo depois vem a palavra where e as implementaes. Quando um outro programa precisar utilizar o mdulo pilha, no incio do script deve-se utilizar a palavra reservada import .

import Pilha ( Pilha (Stack), pilhaVazia, push, pop)

Depois de import deve-se listar as funes que se deseja utilizar do mdulo. Se a lista for omitida todas as funes estaro disponveis:

import Pilha.

75

Andr Rauber Du Bois

7.2 Criando Um ADT

Para tornar o tipo pilha um ADT, basta no incluir na lista de funes pblicas o construtor do tipo:

module Pilha ( Pilha , pilhaVazia, push, pop) where (...) Ento:

Haskell > pop (Stack [1,2]) ERROR: Undefined constructor function "Stack"

Pode-se ento criar uma funo que transforme uma lista em pilha, no mdulo pilha:

listaEmPilha:: [t] -> Pilha t listaEmPilha x = Stack x

e inclui-la na lista de funes:

module Pilha ( Pilha , pilhaVazia, push, pop, listaEmPilha) where

ento:

Haskell > pop (listaEmPilha [1,2]) 1

76

Você também pode gostar