




































































Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Encontra documentos específicos para os exames da tua universidade
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Apostila para Programação Funcional em Haskell
Tipologia: Notas de estudo
1 / 76
Esta página não é visível na pré-visualização
Não perca as partes importantes!





































































CAPÍTULO 1 – Programação em Haskell
1.1 Expressões e Funções
A idéia principal da linguagem Haskell é baseada na avaliação de expressões. A implementação da linguagem avalia (simplifica) a expressão passada pelo programador até sua forma normal. Por exemplo:
Haskell >”Alô Mundo!!” “Alô Mundo!!”
Neste exemplo foi passada para o interpretador Haskell a string (seqüência de caracteres) “Alô Mundo!!”. O sistema respondeu com a mesma seqüência de caracteres, pois esta expressão não pode mais ser avaliada, já encontra-se normalizada. Pode-se utilizar comandos mais complexos:
Haskell> 4 + 3 7
ou
Haskell> ((9*6)+(59/3)) *
Um comando em Haskell é uma fórmula escrita na sintaxe da linguagem. Em Haskell existem várias funções pré-definidas que podem ser usadas para a construção de expressões:
Haskell> reverse “Alô Mundo!!” “!!odnuM ôlA”
A função reverse inverte a ordem dos caracteres em uma string. Apesar de existirem várias funções pré-definidas, a grande idéia da programação funcional é que o usuário defina as suas próprias funções. As funções do usuário são definidas em scripts. Um script contém definições de funções associando nomes com valores e tipos. Em scripts da linguagem Haskell também existem comentários que facilitam uma leitura posterior. Tudo o que for escrito depois de dois travessões (--) é considerado comentário e não é interpretado. Segue um exemplo de script:
-- exemplo.hs -- Neste script apresentam-se algumas definições simples --
idade :: Int -- Um valor inteiro constante idade = 17
maiorDeIdade :: Bool -- Usa a definição de idade maiorDeIdade = (idade>=18)
quadrado :: Int -> Int -- função que eleva um número ao quadrado quadrado x = x * x
mini :: Int -> Int -> Int -- função que mostra o menor valor entre dois inteiros mini a b | a <= b = a | otherwise = b
+, * Soma e multiplicação de inteiros ^ Potência: 2^4 é 16
div Divisão de números inteiros; div 10 3 é 3 mod O resto de uma divisão de inteiros; mod 10 3 é 1 abs Valor absoluto de um inteiro (remove o sinal). negate Muda o sinal de um inteiro. Tabela 2. Funções do Tipo Int
Qualquer operador pode ser usado como função, e qualquer função pode ser usada como um operador, basta incluir o operador entre parênteses (), e a função entre crases ``. Ex:
Haskell> (+) 2 3 5
Haskell> 10 mod 3 1 O programador pode definir os seus próprios operadores em scripts:
Ex: Haskell> 10 &&& 3 3
-- script do meu primeiro operador (&&&) :: Int -> Int -> Int a &&& b | a < b = a | otherwise = b
Pode-se trabalhar com ordenação e igualdade com os números inteiros, assim como com todos os tipos básicos. As funções de ordenação e igualdade tem como argumento dois números inteiros e devolvem um valor do tipo Bool:
Maior que = Maior ou igual == Igual /= Diferente <= Menor ou igual < Menor Tabela 3. Ordenação 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 são:
&& e || ou not negação Tabela 4. Operadores Booleanos
Exemplo de definição utilizando Booleanos:
Os caracteres são 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 número:
Haskell > ‘\65’ ‘A’
Existem funções que transformam um número em caracter, e um caracter em número 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”
Listas podem ser concatenadas usando o operador (++). Ex:
Haskell > “Preciso de” ++ “\nfrases “ ++ “melhores” “Preciso de frases melhores”
A linguagem Haskell permite que se de sinônimos aos nomes de tipos. Exemplo:
type String = [Char]
Isto quer dizer que o tipo String é um sinônimo 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 Números em Ponto Flutuante
Existe em Haskell o tipo Float, que trabalha com números fracionários que são representados em ponto flutuante. Pode-se escrever os números com casas decimais ou utilizando notação científica; 231.6e-2 que significa 231.61 × 10 -2, ou simplesmente 2.3161. O tipo Float além de aceitar os operadores (+, - , *, ^, = =, /=, <=,>=, <, >) vistos anteriormente, possui algumas funções próprias:
Então:
Haskell > verIdade (“André”, 21) 21
1.7 Funções Recursivas
Uma função recursiva é uma função que chama a ela mesma. Grande parte das definições em Haskell serão recursivas, principalmente as que necessitam de algum tipo de repetição. Uma definição recursiva clássica é a do fatorial de um número inteiro positivo: O fatorial de um número inteiro positivo pode ser dividido em dois casos:
Então:
fatorial :: Int -> Int fatorial 0 = 1 (regra 1) fatorial n = n * fatorial (n-1) (regra 2)
Exemplo de avaliação:
fatorial 3 = 3 * (fatorial 2) (2) = 3 * 2 * (fatorial 1) (2) = 3 * 2 * 1 * (fatorial 0) (2) = 3 * 2 * 1 * 1 (1) = 6 Multiplicação
Introduz-se agora um exemplo mais prático de definição recursiva. Seja a função aluno :: Int -> Float, que possui como argumento o número 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 média de notas da turma?
Para se resolver este problema, o ideal é dividi-lo em partes menores. Poderíamos primeiro pensar em uma função soma :: Int -> Float, que soma a nota de todos os alunos. Esta função teria dois casos:
Tem-se então:
soma :: Int -> Float soma 1 = aluno 1 soma n = aluno n + soma (n-1)
Definida a função soma, pode-se definir a função média de maneira simples:
media :: Int -> Float media n = (soma n) / (fromInt n)
Na segunda linha da definição tem-se que usar a função fromInt para transformar o valor n que tem tipo Int, em Float, pois o tipo do operador de divisão é (/) :: Float -> Float -> Float.
Para se imprimir as notas, deve–se imprimir um aluno por linha. Isto pode ser definido recursivamente utilizando uma outra função
imprimeAluno :: Int -> String
Dessa maneira teremos:
imprimeAlunos :: Int -> String
imprimeAlunos 1 = imprimeAluno 1 imprimeAlunos n = imprimeAlunos (n-1) ++ imprimeAluno n
Para a definição das funções imprimeAluno e imprimeMedia é necessário o uso da função pré-definida show, que transforma um número de qualquer tipo em string:
imprimeAluno :: Int -> String imprimeAluno n = show n ++ “ “ ++ show (aluno n) ++ “\n”
imprimeMedia :: Int -> String imprimeMedia n = “\n” ++ “Média da Turma: “ ++ show (media n)
Foram usadas as funções aluno e media definidas anteriormente. Agora apresenta-se o script completo para a função tabela:
-- script tabela
-- banco de dados das notas: aluno :: Int -> Float
aluno 1 = 7. aluno 2 = 10 aluno 3 = 9 aluno 4 = 6. -- (...)
A ordem em que as definições aparecem em um script não é importante. É importante ressaltar que os nomes das funções sempre começam com letras minúsculas, e os tipos com letras maiúsculas.
tabela :: Int -> String tabela n = cabeçalho ++ imprimeAlunos n ++ imprimeMedia n
cabeçalho :: String cabeçalho = “Aluno Nota\n”
imprimeAlunos :: Int -> String imprimeAlunos 1 = imprimeAluno 1 imprimeAlunos n = imprimeAlunos (n-1) ++ imprimeAluno n
imprimeAluno :: Int -> String imprimeAluno n = show n ++ “ “ ++ show (aluno n) ++ “\n”
imprimeMedia :: Int -> String imprimeMedia n = “\n” ++ “Média da Turma: “ ++ show (media n)
soma :: Int -> Float soma 1 = aluno 1 soma n = aluno n + soma (n-1)
media :: Int -> Float media n = (soma n) / (fromInt n)
Haskell > [4 .. 2] []
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 seqüência e deve ser menor ou igual a c.
2.2 Operadores
O operador (:) é o operador de construção de listas. Toda a lista é construída através deste operador, de elementos e de uma lista.
Este operador serve para todo o tipo de listas:
(:) :: Int -> [Int] -> [Int] (:) :: Char -> [Char] -> [Char] (:) :: Bool -> [Bool] -> [Bool] (...)
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 polimórfico e seu tipo é:
(:) :: t -> [t] -> [t]
Onde t é uma variável de tipo que pode ser substituída 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 concatenação (++):
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 variável de tipo. Porém pode-se usar qualquer letra minúscula.
2.3 Funções sobre Listas
Na maioria das definições sobre listas irá se usar a recursão para se percorrer todos os elementos. Uma função simples seria a função para somar todos os elementos de uma lista de números inteiros:
somaLista :: [Int] -> Int
Para esta função existem dois casos: