Escolar Documentos
Profissional Documentos
Cultura Documentos
Program A Cao Haskell
Program A Cao Haskell
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
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:
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
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:
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:
maiorDeIdade :: Bool
maiorDeIdade = (idade>=18)
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:
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).
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
-- script do meu primeiro operador (&&&) :: Int -> Int -> Int a &&& b |a<b | otherwise
=a =b
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:
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
-- ou exclusivo ouEx :: Bool -> Bool -> Bool ouEx x y = (x || y) && not (x && y)
= 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 \ \ \\
Existem funes que transformam um nmero em caracter, e um caracter em nmero inteiro, baseando-se na tabela ASCII. Respectivamente:
Listas de caracteres pertencem ao tipo String, e podem ser representados entre aspas duplas:
Al Mundo!! Haskell
Ex:
10
Isto quer dizer que o tipo String um sinnimo de uma lista de caracteres. Ex:
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
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)
12
Ento:
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
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)
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
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:
Aluno 1 2 3 4
Podese resolver o problema utilizando uma abordagem top-down. A tabela pode ser pensada como sendo uma grande string. Ento
A funo tabela tem como argumento um nmero inteiro (nmero de alunos), e devolve uma string (a tabela). A definio dessa funo seria:
15
Para se imprimir as notas, devese imprimir um aluno por linha. Isto pode ser definido recursivamente utilizando uma outra funo
imprimeAlunos 1 imprimeAlunos 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:
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
imprimeAlunos :: Int -> String imprimeAlunos 1 imprimeAlunos n = imprimeAluno 1 = imprimeAlunos (n-1) ++ imprimeAluno n
soma :: Int -> Float soma 1 soma n = aluno 1 = aluno n + soma (n-1)
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
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:
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]
Um outro caso so as listas vazias, [], que no possuem elementos e podem ser de qualquer tipo:
[] [] []
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:
Haskell > [1 .. 6] 18
Haskell > [4 .. 2] []
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 : []
(:) :: Int -> [Int] -> [Int] (:) :: Char -> [Char] -> [Char] (:) :: Bool -> [Bool] -> [Bool] (...)
19
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 (++):
Aqui se usa a letra t como varivel de tipo. Porm pode-se usar qualquer letra minscula.
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:
Para esta funo existem dois casos: Caso Bsico: Somar os elementos de uma lista vazia [] que ir resultar em 0, e
20
=0 = a + somaLista x
(1) (2)
Ex:
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
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:
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:
=0 = 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
= [] = insere a (ordenacao x)
Utiliza-se para a funo ordenacao uma abordagem top-down. Define-se a funo ordenacao utilizando-se a funo
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:
23
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 ]
[2, 14, 6]
ou
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:
24
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:
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
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)]
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:
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
foldr1
Esta funo coloca um operador entre os elementos de uma lista: foldr1 () [ x1, x2, ..., xn] = x1 x2 ... xn
A definio em Haskell :
=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 (++) [Concatenar ,uma ,lista ,de ,strings ,em ,uma ,s.] Concatenar uma lista de strings em uma s.
27
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:
Ex:
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:
= [] = f a : map f x
Exemplo:
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:
Exemplo:
filter p x = [ a | a <- x, p a]
29
alunos base = map pegaNome (filter nota base) where nota (a,b,c) = c>7 pegaNome (a,b,c) = b
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:
= [] = [] = a : take (n-1) x
30
A funo drop n gera uma lista sem os n primeiros elementos da lista parmetro, sua definio parecida com a da funo take:
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).
31
= []
= 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.
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:
32
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((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
33
Temos:
A estrutura uns
somaOsDoisPrimeiros seja avaliada. Um exemplo interessante de lista infinita a gerada pela funo pr-definida iterate:
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:
Pode-se definir uma funo que pegue todos os valores at a posio n em uma iterao.
34
Ento:
[3 .. ] = [3, 4, 5, 6 ...
Podese definir ento uma funo que ache todas as potncias de um nmero inteiro:
Temse ento
2.8 Erros
Se for passado para a funo take um valor negativo, ela ira devolver uma mensagem de erro:
35
error :: String -> a que pra a avaliao de uma expresso caso ocorra um valor no desejado (). A definio final da funo take seria:
:: Int -> [a] -> [a] = [] = [] = x : take (n-1) xs = error "negative argument"
36
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 x y
x+y
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 = soma 1
37
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)
Os operadores da linguagem podem tambm ser parcialmente aplicados, o que gera os operator sections.
38
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
(op x) y (x op) y
= y op x = x op y
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
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:
f (g x) = (f . g) x
e seu tipo
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
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 x = x+1
\x -> x + 1
Da mesma maneira a funo soma poderia ter sido definida da seguinte maneira:
soma = \ x y -> x + y
41
(.)
f.g
= \x -> f (g x)
42
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:
=0 = 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:
43
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):
iguala (Rua x (Casa y)) (Rua a (Casa b)) iguala (Rua x (Apto y z)) (Rua a (Apto b c)) iguala _ _ = False
Exemplo:
Haskell > iguala (Rua "Abobora" (Apto 13 403)) (Rua "Abobora" (Apto 13 403)) True
44
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:
45
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:
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
Como foi visto at agora, o tipo desta funo poderia ser polimrfico:
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:
Esta definio pode ser lida como Para cada tipo a que pertencer a classe Eq, a funo elem tem tipo a -> [a] -> Bool. 46
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
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]
47
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)
read :: (Read 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
49
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:
As funes que manipulam os tipos algbricos podem ser definidas por pattern matching:
50
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:
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:
= 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:
51
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)
Os tipos algbricos podem ser tambm recursivos. Um exemplo tpico o de construo de rvores:
52
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
12
15
16 1
Pode-se fazer definies recursivas em cima de rvores, como por exemplo uma funo que some todos os elementos:
somaArvore Null
=0
53
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.
=0
ocorrencia (Node valor esq dir) num | valor == num | otherwise = 1 + ocorrencia esq num + ocorrencia dir num = ocorrencia esq num + ocorrencia dir num
54
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:
Um par seria
(...)
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:
55
J a funo somaArvore s funciona para rvores de nmeros inteiros. Por isso o tipo passa a ser:
56
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.
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
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
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).
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
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:
As funes uniao, inter e dif, que fazem respectivamente unio, interseco e diferena de dois conjuntos, possuem o mesmo tipo e definies semelhantes:
59
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
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:
= [] = ins a (sort x)
61
eliminaRep [] = [] eliminaRep [a] eliminaRep (a:b:x) | a == b | otherwise = eliminaRep (b:x) = a : eliminaRep (b:x) = [a]
Exemplos:
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]
conjIgual = (==)
leqConj = (<=)
filterConj = filter
62
card = length
Na verso de map para conjuntos deve-se cuidar os elementos repetidos que podem aparecer:
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:
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
Para se facilitar algumas operaes pode-se instanciar o tipo Conjunto em outras classes de tipos:
63
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
unitario a = [a]
| otherwise = False
uniao [] a uniao a []
=a =a
inter [] a inter a []
= [] = []
65
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 = (<=)
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]
66
mostra = showList
filterConj = filter
foldConj = foldr
card = length
67
CAPTULO 7 IO
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:
sendo que
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:
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
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.
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
pois ela no devolve nenhum valor, simplesmente realiza uma srie de aes de entrada e sada.
7.2 Arquivos
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
return :: a -> IO a.
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
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:
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
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
7.1 Mdulos
Pode-se pensar na implementao da estrutura de dados pilha, como sendo uma lista:
e ento definir-se algumas operaes bsicas sobre ela. A funo push coloca um elemento no topo da pilha:
push x (Stack y)
= Stack (x:y)
pilhaVazia :: Pilha t
pilhaVazia = Stack []
74
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:
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 .
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
Para tornar o tipo pilha um ADT, basta no incluir na lista de funes pblicas o construtor do tipo:
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:
ento:
76