• Nenhum resultado encontrado

Introdução ao Haskell - v1

N/A
N/A
Protected

Academic year: 2021

Share "Introdução ao Haskell - v1"

Copied!
93
0
0

Texto

(1)

Introdução ao Haskell

(2)

Introdução

Programação funcional é um paradigma de programação;

No paradigma imperativo, um programa é uma sequência de

instruções que mudam células na memória;

No paradigma funcional, um programa é um conjunto de definições

de funções que aplicamos a valores

;

Podemos programar num estilo funcional em muitas linguagens

Exemplos: Scheme, ML, O’Caml,

Haskell

, F#, Scala.

(3)

Introdução

O Quicksort em Java

(4)

Introdução

Vantagens

Programas mais concisos;

Próximos de uma especificação matemática;

Excelente modularidade ( polimorfismo, ordem superior, lazy evaluation);

Praticamente todo componente é reutilizável (função);

Demonstrações de correção usando provas matemáticas;

A ordem de execução não afeta os resultados.

(5)

Introdução

Desvantagens

Compiladores/interpretadores mais complexos;

Difícil prever os custos de execução (tempo/espaço);

Alguns algoritmos são mais eficientes quando implementados de forma

imperativa.

(6)

Introdução (Histórico)

1930s

Alonzo Church desenvolve o cálculo-λ, um formalismo matemático

para exprimir computação usando funções;

1950s

Inspirado no cálculo-λ, John McCarthy desenvolve o LISP, uma das

primeiras linguagens de programação;

1970s

Robin Milner desenvolve o ML, a primeira linguagem funcional com

polimorfismo e inferência de tipos;

1970s–1980s

David Turner desenvolve várias linguagens que empregam

(7)

Introdução (Histórico)

1987

Um comitê acadêmico inicia o desenvolvimento do Haskell, uma

linguagem funcional lazy padronizada e aberta;

2003

Publicação do Haskell 98, uma definição padronizada da linguagem;

2010

Publicação do padrão da linguagem Haskell 2010.

(8)

Haskell

Uma linguagem funcional pura de uso genérico;

Nomeada em homenagem ao matemático americano Haskell B. Curry

(1900–1982);

Concebida para ensino e também para o desenvolvimento de aplicações

reais;

Resultado de mais de vinte anos de investigação por uma comunidade de

base acadêmica muito ativa;

Implementações abertas e livremente disponíveis:

http://www.haskell.org

(9)

Haskell (Aplicação)

Utilizações em backend de aplicações web:

Bump mover ficheiros entre smartphones

http://devblog.bu.mp/haskell-at-bump

Janrain plaforma de user management

http://janrain.com/blog/

Chordify extração de acordes musicais

http://chordify.net

Mais exemplos:

(10)

Haskell (Compiladores)

Hugs

Um interpretador interativo de Haskell;

Suporta Haskell 98 e bastantes extensões;

Para aprendizagem e desenvolvimento de pequenos programas;

Disponível em http://www.haskell.org/hugs

(11)

Haskell (Compiladores)

Glasgow Haskell Compiler (GHC)

Compilador que gera código-máquina nativo;

Suporta Haskell 98, Haskell 2010 e bastantes extensões;

Otimização de código, interfaces a outras linguagens, profilling,

grande conjunto de bibliotecas, etc;

Inclui também o interpretador ghci (alternativa ao Hugs)

Disponível em http://www.haskell.org/ghc

(12)

Haskell (Compiladores)

Linux/Mac OS: executar o hugs ou ghci

WinHugs

Utilizando o interpretador: > 2+3*5 17 > (2+3)*5 25 > sqrt (3^2 + 4^2) 5.0

(13)

Haskell (Compiladores)

Alguns comandos básicos

Todo arquivo(script) em Haskell deve ter a extensão

.hs

Comando Significado

:load Carrea um arquivo para execução :reload Recarrega modificações

:edit Edita o arquivo atual

:type <expr> Mostra o tipo de uma expressão :help Obtem ajuda

:quit Termina a sessão

(14)

Haskell (operadores e funções aritméticas)

Operador/função Significado + Adição - Subtração * Multiplicação / Divisão

^ Potência (expoente inteiro) div Quociente (divisão inteira) mod Resto (divisão inteira)

sqrt Raiz quadrada == Igualdade /= Diferença <, >, <=, >= Comparações

(15)

Haskell (convenções sintáticas)

Os argumentos de funções são

separados por espaços

(16)

Haskell (convenções sintáticas)

Um operador pode ser usando como uma função escrevendo-o entre

parêntesis;

Reciprocamente: uma função pode ser usada como operador

escrevendo-a entre crases.

(+) x y = x + y (*) 3 4 = 3 * 4 4 `mod` 2 = mod 4 2 f x `div` n = div (f x) n

(17)

Haskell (convenções sintáticas)

Identificadores

Os nomes de funções e argumentos devem

começar por letras

minúsculas

e podem incluir letras, dígitos, sublinhados e

apóstrofes:

fun1 x_2 y’ fooBar

As seguintes

palavras reservadas

não podem ser usadas como

identificadores:

case class data default deriving do else if import in

infix infixl infixr instance let module newtype of then

(18)

Haskell (convenções sintáticas)

Identação

Todas as definições num mesmo âmbito devem começar na mesma coluna:

(19)

Haskell (convenções sintáticas)

Comentários

Simples: começam por -- até ao final da linha

Várias linhas: delimitados por {- e -}

-- função para somar dois números soma x y = x + y

{- função desatualizada calc x = x `mod` 2 -}

(20)

Haskell (Prelude)

O módulo

Prelude

contém um grande conjunto de funções

pré-definidas:

operadores e funções aritméticas;

funções genéricas sobre listas e muitas outras.

O prelúdio-padrão é automaticamente carregado pelo

interpretador/compilador e pode ser usado em qualquer programa

Haskell.

> head [1,2,3,4] -- obter o 1o elemento

1

> tail [1,2,3,4] -- remover o 1o elemento [2,3,4]

(21)

Haskell (Definindo funções)

> :edit

> :load

> area_quadrado 10 20 > 200

(22)

Haskell (Definindo funções)

funcaoConstante = 12

volume_paralelepipedo b a l = b*a*l soma x y = x + y

soma3 x y z = soma (soma x y) z media3 x y z = (soma3 x y z)/3

(23)

Exercício 01

--1) Faça uma função da média de 4 números sem utilizando utilizar -- o operador mais (+), utilize apenas as funções do slide anterior

--2) Faça uma função para calcular a hipotenusa de um triangulo --

(24)

retângulo-Haskell (Definições locais)

Podemos fazer definições locais usando

where

aos operadores e

funções:

A identação indica o âmbito das declarações; também podemos usar

agrupamento explícito:

(25)

Exercício 02

--1) Faça uma função sem parâmetros de entrada e que defina três

-- variáveis com valores fixos, a qual retorna “true” se os valores -- são iguais.

--2) Faça uma função para calcular a área de um triangulo com lados -- “a”, “b” e “c” utilizando a formula de Heron, mas crie uma -- função local para calcular o “S”.

(26)

Haskell (Tipos de dados)

Um

tipo

é um nome para uma coleção de valores relacionados

Escrevemos para indicar que a expressão “e” admite o tipo T.

Se e :: T, então o resultado de e será um valor de tipo T.

O interpretador

verifica

tipos indicados pelo programador e

infere

tipos

omitidos.

Os programas com erros de tipos são rejeitados antes da execução.

(27)

Haskell (Tipos de dados)

volume_paralelepípedo :: Int -> Int -> Int -> Int volume_paralelepipedo b a l = b*a*l

soma :: Float -> Float -> Float soma x y = soma x y

media3 :: Int -> Int -> Int -> Float media3 x y z = (x + y + z)/3

soma :: (Int,Int) -> Int soma (x,y) = x+y

Uma função faz corresponder valores de um tipo em valores de outro

um tipo

(28)

Haskell (Tipos de dados)

Tipo Descrição

Bool valores lógicos - True, False

Char carateres simples - 'A', 'B', '?', '\n'

String sequências de carateres - "Abba", "UB40"

Int inteiros de precisão fixa (32 ou 64-bits) ex: 142, -1233456 Integer inteiros de precisão arbitrária (limitados pela memória do

computador)

Float vírgula flutuante de precisão simples ex: 3.14154, -1.23e10 Double vírgula flutuante de precisão dupla

(29)

Haskell (Tipos de dados)

Uma

lista

é uma sequência de tamanho variável de elementos

de um mesmo tipo.

(30)

Haskell (Tipos de dados)

Uma

tupla

é uma sequência de tamanho fixo de elementos de

tipos possivelmente diferentes.

Em geral: (T1,T2,...,Tn) é o tipo de tuplas com n componentes de tipos

Ti para i de 1 a n.

(31)

Haskell (Tipos de dados)

Listas

de tamanhos diferentes podem ser do mesmo tipo.

Tuplas

de tamanhos diferentes têm tipos diferentes.

Os elementos de listas e tuplas podem ser quaisquer valores, inclusive

outras listas e tuplas.

['a'] :: [Char] ['b','a','b'] :: [Char] ('a','b') :: (Char,Char) ('b','a','b') :: (Char,Char,Char) [['a'], ['b','c']] :: [[Char]] (1,('a',2)) :: (Int,(Char,Int)) (1, ['a','b']) :: (Int,[Char])

(32)

Haskell (Tipos de dados)

Observações:

A lista vazia [] admite

qualquer

tipo de lista [T ]

A tupla vazia () é o

único valor

do tipo unitário ()

Não existem tuplas com apenas um elemento

(33)

Haskell (Tipos de dados)

Funções polimórficas:

Certas funções operam com valores de quaisquer tipos; tais funções

admitem

tipos com variáveis

.

Uma função diz-se

polimorfa

(“de muitas formas”) se admite um tipo

com variáveis.

A função length calcula o comprimento de uma lista de

valores de

qualquer tipo

a.

(34)

Haskell (Tipos de dados)

Funções polimórficas

Ao aplicar funções polimorfas, as variáveis de tipos são

automaticamente substituídas pelos tipos concretos:

As variáveis de tipo devem começar por uma letra minúscula; é

convencional usar a, b, c, . . .

> length [1,2,3,4] -- Int 4

> length [False,True] -- Bool 2

> length [(0,'X'),(1,'O’)] --(Int,Char) 2

(35)

Haskell (Tipos de dados)

Funções polimórficas

Certas funções operam sobre vários tipos mas não sobre quaisquer tipos:

> sum [1,2,3] 6 > sum [1.5, 0.5, 2.5] 4.5 > sum ['a', 'b', 'c'] ERRO

> sum [True, False] ERRO

(36)

Haskell (Tipos de dados)

Funções polimórficas

Nestes casos o tipo mais geral da função tem restrições de classe

:

“Num a => ...” é uma

restrição de classe

da variável a.

Indica que sum opera apenas sobre tipos a que sejam numéricos.

(37)

Haskell (Tipos de dados)

Funções polimórficas

Algumas classes são

:

Exemplos:

(+) :: Num a => a -> a -> a (/) :: Fractional a => a -> a -> a (==) :: Eq a => a -> a -> Bool (<) :: Ord a => a -> a -> Bool max :: Ord a => a -> a -> a Classe Descrição

Num tipos numéricos (ex: Int, Integer, Float, Double) Integral tipos com divisão inteira (ex: Int, Integer)

Fractional tipos com divisão fracionária (ex: Float, Double) Eq tipos com igualdade

(38)

Haskell (Tipos de dados)

Funções polimórficas

Algumas classes respeitam uma hierarquia:

Ord é uma subclasse de Eq

Num é uma subclasse de Eq

Fractional e Integral são subclasses de

Num

Assim, podemos usar:

== e /= com tipos em Ord ou em Num

+, - e * com tipos em Fractional ou em

Integral

Em alguns casos será necessário converter elementos de uma classe em outra, exemplo:

(39)

Haskell (Expressões condicionais)

Podemos exprimir uma condição com duas alternativas usando

if. . . then. . . else. . .

’.

As expressões condicionais podem ser encadeadas:

Em Haskell, ao contrário do C/C++/Java, a alternativa ‘else’ é

obrigatória

abs :: Float -> Float

abs x = if x >= 0 then x else -x

sinal :: Int -> Int

sinal x = if x > 0 then 1 else

(40)

Haskell (Expressões condicionais)

Podemos usar

guardas

em vez de expressões condicionais:

Testa as condições pela ordem no programa.

Seleciona a primeira alternativa verdadeira.

Se nenhuma condição for verdadeira: erro de execução.

A condição ‘otherwise’ é um sinónimo de True

sinal :: Int -> Int sinal x | x > 0 = 1

| x == 0 = 0

(41)

Haskell (Expressões condicionais)

Pode-se usar

múltiplas equações

encontrar uma solução:

Pode-se usar também

padrões

ou

variáveis anônimas

para melhorar a

solução acima

ou :: Bool -> Bool -> Bool ou False False = False ou True False = True ou False True = True ou True True = True

ou' :: Bool -> Bool -> Bool ou' False False = False ou' _ _ = True

(42)

Lista de Exercício 01

1) Faça uma função que calcule a distância entre dois pontos

2) Faça uma função para verificar se um ano informado é bissexto ou não.

3) Defina uma função que recebe três números inteiros representando, respectivamente, um dia, um mês e um ano e verifica se os números formam uma data válida.

4) Crie uma função par::Int->Bool para verificar se um numero é par ou impar. 5) Escreva a função conceito :: Float -> Char que recebe uma nota e retorne o conceito correspondentes conforme as regras abaixo:

Nota abaixo de 4 – Conceito E, Nota entre 4 e 5.99 conceito D, Nota entre 6 e 7.49 conceito C, Intervalo entre 7.5 e 8.99 conceito B e acima de 9 conceito A.

(43)

Haskell (Tuplas)

As

tuplas

permitem a definição e o uso de tipos de dados heterogêneos sob

uma estrutura relacional. As tuplas são representadas em scripts por listas

de componentes separados por vírgula, entre parênteses.

O Prelude fornece duas funções para retornar respectivamente o primeiro e

o segundo elemento:

Sua implementação provavelmente utiliza variáveis anônimas:

("JOSE",1.8,23) ou (100,10.4,”Brasil”)

fst :: (a,b) -> a ou snd :: (a,b) -> b

(44)

Haskell (Tuplas)

Pode-se utilizar

tuplas

e a palavra reservada

type

para definir tipos de

dados mais complexos:

Com uso de variáveis anônimas podemos buscar informações específicas nas

tuplas:

type Seq = String

type Nomes = (Seq, Seq, Seq, Seq) --funções constantes

f_nomes_estacoes :: Nomes f_nomes_estacoes =

("Inverno","Outono","Primavera","Verao")

primeiro :: Nomes -> Seq primeiro (x, _, _, _) = x segundo :: Nomes -> Seq segundo (_, x, _, _) = x

(45)

Exercício 03

--1) Crie um trecho de script que contenha a definição de tipo para nome, idade, peso e esporte praticado. Logo em seguida defina outro tipo de dados que represente pessoas com as quatro informações acima. --2) Crie uma função chamada bancoDeDados que recebe um “id” do tipo inteiro e retorne uma pessoa. Dentro dessa função faça uma instrução condicional para várias pessoas diferentes com identificadores

diferentes.

--3) Crie funções para recuperar o nome, a idade, o peso e o esporte preferido de um tipo pessoa.

--4) Crie uma função que recebe duas pessoas e retorne o nome da mais nova, faça uma função também para retornar a mais leve.

--5) No console, busque o nome da pessoa de código 4 que está na função que representa o banco de dados.

(46)

Haskell (Recursão)

Pode-se definir funções usando outras previamente definidas.

Contudo, pode-se definir uma função por recorrência, isto é, usando a

própria função que estamos a definir; tais definições dizem-se recursivas.

Exemplo:

fatorial :: Int -> Int fatorial 0 = 1 fatorial n = fatorial (n-1) * n --fatorial 5 --(fatorial 4) * 5 --((fatorial 3) * 4) * 5 --(((fatorial 2) * 3) * 4) * 5 --((((fatorial 1) * 2) * 3) * 4) * 5 --(((((fatorial 0) * 1) * 2) * 3) * 4) * 5 --((((1 * 1) * 2) * 3) * 4) * 5 --(((1 * 2) * 3) * 4) * 5 --((2 * 3) * 4) * 5 --(6 * 4) * 5 --24 * 5 --120

(47)

Haskell (Recursão)

Alternativas de implementação:

Utilizando guardar:

Utilizando condicional:

Porque recursão?

fatorial n | n==0 = 1 | otherwise = n * fatorial (n-1)

fatorial n = if n==0 then 1 else n*fatorial (n-1)

• Exprimir a solução de um problema usando problemas semelhantes mas de menor tamanho. • Modelo universal de computação: qualquer algoritmo pode ser escrito usando funções recursivas. • Podemos demonstrar propriedades de funções recursivas usando indução matemática.

(48)

Haskell (Recursão)

Exemplos:

N-ésimo termo de fibonnacci:

Quantos números múltiplos de sete existem entre zero e um n-ésimo

número:

fibonacci :: Int -> Int fibonacci 0 = 0

fibonacci 1 = 1

fibonacci n = (fibonacci (n-1)) + (fibonacci (n-2))

multiplo7 :: Int -> Int multiplo7 7 = 1

multiplo7 n | n<=6 = 0

(49)

Exercício 04

--1) Crie uma função recursiva para calcular a potencia de um número.

--2) Crie uma função recursiva para dizer se um número é par. Essa função deve retornar True ou False.

--3) Crie funções recursiva para informar o valor de um somatório dado um número N.

--4) Com base na questão dois do Exercício 3 (slides anteriores), faça

funções recursivas que dado o “id” limite, deve-se percorrer todos os dados do banco de dados do primeiro até esse valor final e retorne:

a) Quantas pessoas foram selecionadas na base de dados b) Qual a menor idade da base

c) Qual a soma da idade das pessoas d) Qual a média de idade

e) Quantos pessoas estão acima da média de idade

5) somaDigitos: recebe um número natural e retorna a soma de seus dígitos. Ex.: somaDigitos 3284 ==> 17

(50)

Haskell (Listas)

Listas são coleções de elementos:

em que a

ordem é significativa

elementos de um

mesmo tipo

possivelmente com elementos repetidos

Uma lista em Haskell

ou é vazia [];

ou é x:xs (x seguido da lista xs)

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

O operador :forma uma nova lista desde que que o último elemento seja uma

(51)

Haskell (Listas)

Pode-se concatenar listas do mesmo tipo com o operador

++

O tipo String na verdade é uma lista do tipo char: [Char]

Algumas funções do Prelude para manipular String

> ['O','l','a'] == "Ola" True

> [(1,'b'),(2,'c')] ++ [(3,'a'), (4,'w')] [(1,'b'),(2,'c'),(3,'a'),(4,'w')]

> words "O rato roeu a roupa do rei de roma"

["O","rato","roeu","a","roupa","do","rei","de","roma"] > unwords

["Um","prato","de","trigo","para","tres","tigres"] "Um prato de trigo para tres tigres"

(52)

Haskell (Listas)

Pode-se definir listas com

sequências aritméticas

da forma [a..b] onde

a e b são números:

Pode-se definir

listas infinitas

:

> [1..10] [1,2,3,4,5,6,7,8,9,10] > [1,3..10] [1,3,5,7,9] > [10,9..1] [10,9,8,7,6,5,4,3,2,1] take 10 [1,3..] [1,3,5,7,9,11,13,15,17,19] > [1,3..] !! 4 9

(53)

Haskell (Listas)

A manipulação das listas é muitas da vezes feita através de recursão

separando a cabeça e cauda:

produtorio :: [Int] -> Int produtorio [] = 1

produtorio (x:xs) = x * produtorio xs quantidade :: [a] -> Int

quantidade [] = 0

(54)

Exercício 05

--1) Crie uma função que retorne uma lista com todas as letras do alfabeto. --2) Crie uma função que retorne uma lista com os números de 0 a 200 de

forma decrescente.

--3) Crie uma função para retornar o inverso de uma lista.

--4) Crie uma função para pegar os “n” primeiros elementos de uma lista --5) Crie uma função para remover os “n” primeiros elementos de uma lista --6) Crie uma função que remove o último elemento de uma lista

--7) Crie uma função que remove os “n” últimos elementos de uma lista --8) Crie uma função que remove o n-ésimo elemento de uma lista

(55)

Haskell (Listas - Compressão)

Em matemática é usual definir conjuntos a partir de outro usando notação

em compreensão. Exemplo:

o qual define o conjunto:

Em Haskell podemos definir uma lista a partir de outra usando uma

notação semelhante. Exemplo:

> [x^2 | x<-[1,2,3,4,5]] [1, 4, 9, 16, 25]

(56)

Haskell (Listas - Compressão)

Um termo “padrão<-lista” chama-se um

gerador

:

determina os valores das variáveis no padrão;

determina a ordem dos valores gerados.

Exemplo:

Pode-se usar múltiplos geradores:

> [x | x <-[1..10] ] [1,2,3,4,5,6,7,8,9,10] > [x | x <-[1,4..300] ] [1,4,7,10,13,16,19,22,25,28] > [x | x <-[1,7..20] ] [1,7,13,19] > [(x,y) | x<-[1,2,3], y<-[4,5]] [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]

(57)

Haskell (Listas - Compressão)

Ordem entre geradores:

As variáveis dos geradores posteriores mudam primeiro;

Analogia: ciclos ‘for’ embutidos:

> [(x,y) | x<-[1,2,3], y<-[4,5]] -- x primeiro [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]

> [(x,y) | y<-[4,5], x<-[1,2,3]] -- y primeiro [(1,4),(2,4),(3,4),(1,5),(2,5),(3,5)]

(58)

Haskell (Listas - Compressão)

Dependências entre geradores:

Um exemplo: a função concat (do prelúdio-padrão) concatena

uma lista de listas, exemplo:

Podemos definir usando uma lista em compreensão:

> [(x,y) | x<-[1..3], y<-[x..3]]

[(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]

Os geradores podem depender dos valores anteriores mas não

dos posteriores.

> concat [[1,2,3],[4,5],[6,7]] [1,2,3,4,5,6,7]

concat :: [[a]] -> [a]

(59)

Haskell (Listas - Compressão)

Guardas

As definições em compreensão podem incluir condições (designadas

guardas) para filtrar os resultados.

Exemplo: Conjunto dos inteiros x, tal que x está entre 1 e 10 e x é par.

Divisores de um num número inteiro positivo:

> [x | x<-[1..10], x`mod`2==0] [2,4,6,8,10]

divisores :: Int -> [Int]

(60)

Haskell (Listas - Compressão)

Exemplos:

--Um número é primo se é divisível por um e por eleprimo :: Int -> Bool primo n = divisores n == [1,n]

--Vamos verificar se uma lista está em ordem crescente zipar (a:as) (b:bs) = (a,b) : zipar as bs

zipar _ _ = []

pares :: [a] -> [(a,a)]

pares xs = zipar xs (tail xs) crescente :: Ord a => [a] -> Bool

crescente xs = and [x<=x' | (x,x')<-pares xs]

--Listar todos os números primos entre 2 e N primos :: Int -> [Int]

primos n = [x | x<-[2..n], primo x]

Combina duas listas na lista dos pares de elementos

correspondentes. Exemplo: > pares [1,2,3,4] [(1,2),(2,3),(3,4)] Usasmos a função and do Prelude.

(61)

Haskell (Listas - Compressão)

Exemplos:

--Procurar um valor numa lista e obter todos os seus índices indices :: Eq a => a -> [a] -> [Int]

indices x ys = [i | (i,y)<-zip [0..n] ys, x==y] where n = length ys - 1

--Rescreva a função anterior com as funções do módulo Char minusculass :: String -> Int

minusculass cs = length [c | c<-cs, isLower c]

--Retornar quantas letras de uma string são minúsculas minusculas :: String -> Int

minusculas txt = length [c | c<-txt, c>='a' && c<='z']

No início do arquivo digite: Import Char

--Escreva uma função para converter uma String para maiúscula stringUpper :: String -> String

stringUpper cs = [toUpper c | c<-cs]

--Escreva uma função para escrever uma string com condição.

(62)

Exercício 06

--1) Crie uma função que recebe dois parâmetros: uma lista de inteiros e um número que representa a operação sobre a lista (1-lista de números pares e 2-lista de números impares). OBS: use as funções odd e even.

--2) Crie uma função que recebe uma lista de tuplas do tipo (Int,Int) e retorne uma lista com a soma das tuplas. Ex: [(1,2),(4,7)] => [3,11].

--3) Crie uma função que recebe duas listas e retorne outra lista com a combinação de todos os elementos em pares.Ex [1,2] [3] => [(1,3),(2,3)] --4) Crie uma função que remove um determinado caractere de uma string

--5) Crie uma função que retorne o código dos alunos que tenham nota acima de oito, essa função deve manipular o retorno da função: baseDeDados = [ (1, “André”, 10.0), (2, “Carlos”, 6.8), (3,”Maurício”, 7.0)]

--6) Crie uma função que recebe uma lista com vários nomes e verifica se alguns desses nomes começam com o parâmetro de entrada N.

--7) Para calcular as combinações de notas para devolver o troco durante um pagamento, podemos definir a função

(63)

Haskell (Listas)

Por causa da

lazy evaluation

as listas são calculadas à medida da

necessidade e apenas até onde for necessário.

Algumas funções do prelúdio-padrão produzem especificamente listas

infinitas:

-- a lista infinita 1,1,.. uns :: [Int] uns = 1 : uns > head (uns) repeat :: a -> [a] -- repeat x = [x,x,x,...] cycle :: [a] -> [a]

-- cycle xs = xs++xs++xs++... > take 10 (repeat 1)

(64)

Haskell (Listas)

Porquê usar listas infinitas?

Permite simplificar o processamento de listas finitas combinando-as com

listas infinitas (por ex.: evita especificar comprimento).

Um exemplo simples: escrever uma função preencher :: Int -> String ->

String que preenche uma cadeia com espaços de forma a perfazer n

caracteres.

Solução:

preencher n xs = take n (xs++repeat ' ') > preencher 10 "Haskell"

"Haskell "

> preencher 10 "Haskell B. Curry" "Haskell B."

(65)

Haskell (Listas)

Pode-se converter valores de qualquer tipo para String com a função

show

:

Pode-se também converter valores de Strting para Fractional através da

função

read

:

Uma função útil é a

putStr

, ela imprime uma String no console

> show 98 "98“

> read "77" + 3 80

> putStr " 2 tabs uma\t\tsaída" 2 tabs uma saída

Caractere Efeito \t Tabulação \n Nova linha \\ Imprime barra \’ Imp. aspas simples \” Imp. aspas duplas > show "Ola"

"\"Ola\"" > " 7 ao quadrado "++ show (7*7)“7 ao quadrado 49"

> putStr "Saltando\nde linha" Saltando

(66)

Haskell (Listas)

Algumas funções

(67)

Haskell (Funções de alta ordem)

Uma função é de

ordem superior

se tem um argumento que é

uma função ou um resultado que é uma função.

Exemplo: o primeiro argumento de twice é uma função:

twice :: (a -> a) -> a -> a twice f x = f (f x) dobra x = 2 * x Triplica x = 3 * x > twice dobra 10 40 > twice triplica 10 90

Agora funções são consideradas com os mesmos direitos de qualquer outro tipo,

assim elas podem ser passadas por parâmetro ou podem ser retornadas como resultado de uma outra função.

(68)

Haskell (Funções de alta ordem)

Outro exemplo:

Vantagens:

Permite definir

padrões de computação

comuns que podem ser facilmente

reutilizados;

Facilita a definição de

bibliotecas para domínios específicos.

app :: (a->b) -> (a,a) -> (b,b) app f (x,y) = (f x, f y)

> app chr (65, 70) ('A','F’)

> app tan (65, 70)

(69)

Haskell (Funções de alta ordem)

A função

mult

abaixo, pode ser entendida como tendo dois argumentos de

entra e um de retorna , Mas na realidade

mult

é uma função que recebe um

argumento do tipo Int e devolve uma função do tipo (Int->Int).

Em Haskell, todas as funções são unárias!

mult 2 5 ≡ (mult 2) 5 :: Int

Assim, mult pode ser usada pra gerar novas funções:

mult :: Int -> Int -> Int mult x y = x * y

Curring

: aplicação de funções parciais

dobrar = mult 2 triplicar = mult 3 > dobrar 30

60

(70)

Haskell (Funções de alta ordem)

Os

operadores infixados

também podem ser aplicados a apenas um

argumento, gerando assim uma nova função.

Exemplos:

(<=) :: Integer -> Integer -> Bool > (0<=) 10

True

(+) :: Integer -> Integer -> Integer > (5+) 9

14

(*) :: Double -> Double -> Double > (3*)7

(71)

Haskell (Funções de alta ordem)

A maioria das definições sobre listas se encaixam em três casos:

folding colocação de um operador entre os elementos de uma lista;

filtering filtra alguns elementos da lista;

mapping a aplicação de funções a todos os elementos da lista.

Exemplo de aplicação de

high order functions

(72)

Haskell (Funções de alta ordem)

Veja que as funções abaixo tem um padrão de computação:

Para esse padrão existe a seguinte definição:

Utilizando a função de alta ordem temos:

Folding

sum [] = 0

sum (x:xs) = x + sum xs and [] = Trueand (x:xs) = x && and xs

conc [] = []

conc (x:xs) = x ++ conc xs

> foldr (+) 0 [1..5]

15 > foldr (&&) False []False

concat l = foldr (++) [] l > concat [“Hi, ”,”man”]

Padrão, aplicamos um operador entre cada elemento de uma lista

(73)

Haskell (Funções de alta ordem)

Veja que as funções abaixo tem um padrão de computação:

Para esse padrão existe a seguinte definição:

Utilizando a função de alta ordem temos:

Maping

ft [] = [] ft (x:xs) = fatorial x : ft xs min [] = [] min (x:xs) = toLower x : min xs trip [] = [] trip (x:xs) = (3*x) : trip xs > map fatorial [1..5] [1,2,6,24,120] > map toLower [‘X’,’B’]“xb” > map (3*) [1..5] [3,6,9,12,15]

Padrão, aplicamos uma função a cada elemento da lista

(74)

digit [] = []

digit (x:xs) | isDigit x = x:digit xs | otherwise = digit xs

Haskell (Funções de alta ordem)

Veja que as funções abaixo tem um padrão de computação:

Para esse padrão existe a seguinte definição:

Utilizando a função de alta ordem temos:

Filtering

Padrão, filtramos elementos da lista de acordo com uma

função de critério. aprov [] = [] aprov (x:xs) = if x>=6 then x:aprov xs else aprov xs > filter (6<=) [2,9,10,5,9] [9,10,9]

> filter isDigit "abc123cdf" "123"

(75)

Haskell (Funções de alta ordem)

Alonzo Church inventou um sistema formal, chamado λ-calculus (Lambda

Cálculo) , e definiu a noção de função computável utilizando este sistema.

O Lambda Cálculo pode ser chamado “a menor linguagem de programação

universal” do mundo. As linguagens funcionais são baseadas nesse conceito.

O lambda cálculo pode ser visto como uma linguagem de programação

abstrata em que funções podem ser combinadas para formar outras

funções, de uma forma pura.

Como dito anteriormente, o lambda cálculo trata funções como cidadãos de

primeira classe, isto é, entidades que podem, como um dado qualquer, ser

utilizadas como argumentos e retornadas como valores de outras funções.

Funções anônimas (Conceitos)

(76)

Haskell (Funções de alta ordem)

Uma abstração lambda é um tipo de expressão que denota uma função:

O λ determina que existe uma função, e é imediatamente seguido por uma

variável, denominada parâmetro formal da função.

Abstração Lambda:

Em Haskell:

Funções anônimas (Conceitos)

(λx. + x1)

λy. π * y * y

> (\y -> pi * y * y) 3 28.2743338823081

(77)

Haskell (Funções de alta ordem)

Em Haskell, é possível definir novas funções inspiradas no conceito de

abstrações lambda (λ)

da forma:

Exemplos:

Funções com mais de uma parâmetro são da forma:

Exemplos:

Funções anônimas

\x -> e Representa uma função com

parâmetro x e corpo “e”

\x … pn -> e > (\x -> x+x) 5 10 > (\y -> y*y) 416 > (\x -> x:x^2:[]) 2 [2:4] > (\(x:xs) y-> y:xs) [1,2,3,4,5,6] 7

[7,2,3,4,5,6] > (\(x1,y1) (x2,y2) -> (x1,y2)) (0,3) (6,9)(0,9)

(78)

Haskell (Funções de alta ordem)

É possível utilizar funções anônimas na definição de outras funções:

Exemplos:

As funções anônimas são úteis para evitar a declaração de funções auxiliares.

Exemplos:

Operadores infixados sob um argumento são na verdade funções anônimas:

Funções anônimas

dobro = \x -> x+x > dobro 5 10 cauda = \(_:c) -> c > calda [1,2,3,4] [2,3,4]

trocaPares xs = map troca xs

where troca (x,y) = (y,x)

trocaPares1 xs = map (\(x,y) -> (y,x)) xs

(79)

Haskell (Funções de alta ordem)

Exemplo:

Funções anônimas

-- Nº Aluno, Nome, Nota

type Aluno = (Int, String, Float) type Curso = [Aluno]

listaAlunos :: Curso

listaAlunos = [(1234, "Jose Azevedo", 13.2), (2345, "Carlos Silva", 9.7),(3456, "Rosa Mota", 17.9)]

mediaDasNotas :: Curso->Float

mediaDasNotas lista = (/) (sum (map (\(_,_,n)->n) lista)) (fromIntegral (length lista)) > mediaDasNotas listaAlunos

(80)

Exercício 07

--1) Usando funções de alta-ordem, crie uma função ou um comando no prompt que receba uma lista de strings e retorne uma lista com o tamanho de cada string da lista de entrada.

--2) Crie uma função de alta ordem chamada takeWhile, a qual recebe uma função de filtragem e uma lista, o objetos é retorna a lista de acordo com a filtragem. --3) a) Implementar uma função que calcula o sucessor de um número inteiro

usando expressão lambda (λx. x+1). Em seguida, b) definir uma função duasVezes para aplicar uma função nela mesma. Finalmente, c) construir uma função para mapear a aplicação de sucessor e d) duasVezes sobre uma lista de inteiros. Faça isso de forma recursiva, com compreensão de lista e com uma função de alta

ordem. e) Por fim, dê dois exemplos do uso das funções de mapeamento com funções lambdas.

--4) Usando o conceito de curring e a função abaixo, crie uma função chamada “incrementa”, a qual irá incrementar o valor passado como parâmetro em 1. soma :: Int -> Int -> Int

(81)

Exercício 07

--5) Usando o mesmo conceito de curring do exercício anterior, faça uma função que incremente os elementos de uma lista de inteiros.

--6) Utilizando map, crie funções que convertas os valores da esquerda nos da direita:

--[0,1,4,9] -> [0.0,1.0,2.0,3.0] --"HAL" -> "IBM"

--["bom","dia","turma"] -> "bdt" --["ciênca", "da", "computação"] -> [6,2,10]

--7) Crie tipos para representar Pessoa, Livro e uma lista de livros alugados pelas pessoas. Depois crie funções da forma:

a) emprestadosPorPessoa :: Database -> Person -> [Book] b) emprestadosPorLivro :: Database -> Book ->[Person] c) estaEmprestado :: Database -> Book -> Bool

(82)

Haskell (IO)

Um programa Haskell está organizado em módulos;

Cada módulo é uma coleção de funções e tipos de dados definidos em um

ambiente privado

Um módulo pode exportar todas ou determinadas definições:

Ao iniciar o interpretador, o módulo Prelude já é carregado para execução.

Módulos

module Nome (definições_a_serem_exportadas) where <importações>

(83)

Haskell (IO)

Depois que um módulo é carregado pelo comando load, as definições

disponíveis passam a estar disponíveis no ambiente junto com o Prelude;

Módulos

module Temp where

cel2Far c = c * 1.8 + 32 kel2Cel k = k - 273

kel2Far k = cel2Far (kel2Cel k)

> :load "C:\\Program Files (x86)\\WinHugs\\packages\\hugsbase\\Temp.hs"

module Exemplo where import Char

letra :: Int -> Char

letra n = if (n>=65 && n<=90)||(n>=97 && n<= 122) then chr n

else ' '

Por default, se nada for indicado para exportação,

todas as definições serão públicas.

Pode-se importar somente certa função do módulo: import Char (chr, toLower)

(84)

Haskell (IO)

Para criar um programa executável com o compilador Haskell, precisa-se de

um módulo principal com uma função main, a qual retorna um tipo IO;

A função main é o ponto de entrada no programa, pois é ela que é invocada

quanto o programa comilado é executado

A compilação usando GHC, pode ser feita executando o seguinte comando

no shell:

Compilando programas Haskell

module Main where main :: IO()

main = putStr "Olá mundo!!"

(85)

Haskell (IO)

Haskell, como as outras linguagens de programação, possui funções

que se comunicam com o sistema operacional, afim de realizar entrada

e saída de dados.

Tais funções devem retornar um valor do tipo (IO t), aonde t significa

um tipo de dado qualquer.

Interação com usuários e bloco de comandos

putStr :: String IO() putStrLn :: String IO() getLine :: IO(String)

Os parênteses é um tipo de dado que ter apenas um valor, o qual significa a

ausência de dados.

Nesse caso a operação de entrada e saída retorna uma String.

(86)

Haskell (IO)

Um programa é compostos por um conjunto de ações que podem

acionar diversas funções, para dar suporte a essa necessidade, o

Haskell usa o conceito de blocos de ação, através do comando

do

:

Interação com usuários e bloco de comandos

menu :: IO()

menu = do putStrLn "1-Incluir cliente" putStrLn "2-Alterar cliente" putStrLn "3-Excluir cliente" putStrLn "0-Sair"

imprimeString :: String -> IO() imprimeString [] = return ()

imprimeString (x:xs) = do putChar x

imprimeString xs

Essa palavra não retorna um valor e sim converte um valor do tipo “a” em um tipo

(87)

Haskell (IO)

As funções getChar e getLine pode trabalhar com o operador “<-” para

atribuir o valor capturado do teclado para uma função:

Interação com usuários e bloco de comandos

concatenalinhas :: IO()

concatenalinhas = do str1 <- getLine str2 <- getLine

putStrLn (str1 ++ str2)

ask :: String -> IO String

ask question = do putStr question getLine

principal :: IO ()

principal = do nome <- ask "Qual é o seu nome? " matr <- ask "Qual é sua matrícula? " putStrLn ("Bem-vindo "++ nome ++ "!") putStrLn ("Seu número é "++ matr)

(88)

Haskell (IO)

Outros exemplos:

Interação com usuários e bloco de comandos

lerString :: IO String lerString = do x <- getChar if x == '\n' then return [] else do xs <-lerString return (x:xs) executar :: IO() executar = do x <- getLine

let a = map toUpper x b = map toLower x putStr a

putStr "\n" putStr b

O comando let define funções locais.

calcular :: IO()

calcular = do putStr "Digite um numero: " n <- getLine

let x = ((read n)::Double) s = sin x

c = cos x

putStr ("O seno de "++n++" e' "++(show s)++['.','\n']++ "O cosseno de "++n++" e' "++(show c)++['.','\n'])

(89)

Haskell (IO)

Lendo e escrevendo outros tidos de dados (readLn e print):

Interação com usuários e bloco de comandos

leInt :: IO(Int)

leInt = do putStr "Digite um valor inteiro: " readLn

leInt2 :: IO(Int)

leInt2 = do putStr "Digite um valor inteiro: " n <- getLine return (read n) Transforma em String soma :: IO () soma = do n1 <- leInt n2 <- leInt

putStr "A soma e': " print (n1+n2)

As funções read, readLn e print possuem as seguintes assinaturas:

lista :: IO ()

lista = do putStr "Digite um valor: " x <- readLn

(90)

Haskell (IO)

Outro exemplo:

Interação com usuários e bloco de comandos

menuAdivinhe = do num <- randomRIO(1::Int,100)

putStrLn "Gerando numero entre 1 e 100 ..." adivinhe1 num

adivinhe1 num = do putStr "Tente descobrir (digite um numero):" numP <- getLine

if (read numP) < num

then do putStrLn "Muito baixo" adivinhe1 num

else if (read numP) > num

then do putStrLn "Muito alto" adivinhe1 num

(91)

Haskell (IO)

Outro exemplo (while):

Interação com usuários e bloco de comandos

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] le_imprime :: IO()

le_imprime = do entrada <-getLine if entrada == []

then return ()

else do putStrLn entrada le_imprime

Funão que recebe uma lista de strings, e a transforma em

uma string colocando o caracter de nova linha (‘\n’)

(92)

Exercício8

--1) Escreva um programa que lê uma linha, a partir do teclado, verifica se ela contém apenas caracteres alfabéticos e imprime essa linha na tela, com as

palavras em ordem inversa. Caso a linha contenha algum caractere não alfabético, imprime uma mensagem de erro.

--2) Escreva um programa que pergunta ao usuário o seu nome e telefone e imprime na tela a informação obtida, em uma única linha.

--3) Escreva um programa que lê várias linhas a partir do teclado, e imprime cada linha lida, com os caracteres convertidos para maiúsculas, até que seja digitada uma linha nula.

--4) Defina uma função leIntList que lê para uma sequência de valores inteiros do dispositivo de entrada padrão, até que seja digitado o valor 0, e retorna a lista dos valores lidos.

--5) Refaça o exercício anterior, supondo que os números devem ser digitados todos em uma única linha.

--6) Defina um programa que lê uma valor inteiro positivo n e imprime a lista de pares (i, i2), para valores de i no intervalo 1 ≤ i ≤ n.

(93)

Referencias

Livros

Material online

Slides do professor Pedro Vasconcelos DCC/FCUP http://www.dcc.fc.up.pt/~pbv/aulas/pf/

Referências

Documentos relacionados

Nessa situação temos claramente a relação de tecnovívio apresentado por Dubatti (2012) operando, visto que nessa experiência ambos os atores tra- çam um diálogo que não se dá

Para a parede A, concluiu-se que o revestimento de argamassa é muito importante para a isolação sonora de paredes, constatando-se um aumento de 5 dB no Índice de Redução

Todo ser humano é único e, por isso, toda sala de aula é um berço de diversidade. O que os sistemas educacionais fizeram ao longo dos tempos foi homogeneizar o sistema educacional

Não pode ser copiado, escaneado, ou duplicado, no todo ou em parte, exceto para uso como permitido em uma licença distribuída com um certo produto ou serviço ou de outra forma em

Compreendendo- se que o estudo dos eventos do século XX podem ser relevantes e esclarecedores para a compreensão da história e da sociedade de hoje, e levando-se em conta o

Intervalo de ínicio da câmara úmida h Figura 1 – Supressão da esporulação de Botrytis cinerea por isolados de Clonostachys rosea em folhas de morangueiro, submetidas a

E as mineradoras da Região Carbonífera do Sul de Santa Catarina, que durante décadas exploraram sem muitos cuidados o carvão mineral, estão dentro desse contexto.Com base em

Contudo, não é possível imaginar que essas formas de pensar e agir, tanto a orientada à Sustentabilidade quanto a tradicional cartesiana, se fomentariam nos indivíduos