Programação Funcional - com Haskellfolivetti.github.io/courses/BigData/PDF/Aula02.pdf ·...

176
Programa¸ ao Funcional com Haskell Fabr´ ıcio Olivetti de Fran¸ ca Universidade Federal do ABC

Transcript of Programação Funcional - com Haskellfolivetti.github.io/courses/BigData/PDF/Aula02.pdf ·...

Programacao Funcional

com Haskell

Fabrıcio Olivetti de Franca

Universidade Federal do ABC

Paradigma Funcional

Paradigmas

Em muitos cursos de Computacao e Engenharia iniciam com paradigma

imperativo.

Exemplo classico da receita de bolo.

1

Paradigma Imperativo

int pares[10];

for (int i=0; i<10; i++) {

pares[i] = 2*i;

}

2

Paradigma Imperativo

• Descrevo passo a passo o que deve ser feito.

• Infame goto.

• Evoluiu para o procedural e estruturado com if, while, for.

3

Paradigma Imperativo

int pares[10];

for (int i=0; i<10; i++) {

pares[i] = dobro();

}

int dobro () {

static int i = 0;

++i;

return i;

}

4

Problemas

Ao seguir o passo a passo, voce chega no resultado...mas nao tem ideia

de qual sera ele.

Qualquer um pode usar suas variaveis globais e suas funcoes, para o seu

uso intencional ou nao...

5

Orientacao a Objetos

Outro paradigma muito estudado em cursos de Computacao.

Encapsula dados com seus proprios metodos.

Evita que certas informacoes e procedimentos sejam utilizados fora de

seu contexto.

6

Orientacao a Objetos

class Pares {

private int i; // n~ao quero ninguem mudando meu estado

private ArrayList lista;

public Pares() {

i=0;

lista = new ArrayList();

}

public void addOne() {

++i;

lista.add(2*i);

}

public ArrayList get(){

return lista;

}

}

7

Orientacao a Objetos

class Main {

public static void main(String[] args) {

Pares l1 = new Pares();

l1.addOne();

l1.addOne();

l1.addOne();

System.out.println(l1.get());

}

}

8

Problemas

9

Problemas

Nos textos didaticos temos exemplos simples e criados para ilustrar

quando faz sentido termos objetos.

Nem sempre isso representa a realidade.

Composicao de funcoes atraves de heranca = bagunca!

10

Problemas

Encapsula codigos imperativos para seguranca e reuso, mas nao evita os

bugs na criacao das classes.

Usa estado intensivamente, incentiva mutabilidade e nao-determinismo.

Difıcil de paralelizar.

11

Efeito colateral

A execucao da instrucao atual depende do estado atual do sistema.

Ex.: dobro() e addOne() vai depender do estado atual de i.

12

Efeito colateral

Eu posso executar a funcao dobro() para o primeiro e o segundo

elemento em paralelo??

13

Paradigma Funcional

• Computacao = avaliacao de composicao de funcoes.

• Evita estados.

• Declarativo.

14

Paradigma Funcional

take 10 [2*i | i <- zplus]

-- pegue 10 elementos da lista formada pelo

-- dobro dos numeros inteiros positivos.

15

Paradigma Funcional

Note que i nao e uma variavel, nao muda de valor, e apenas um sımbolo

da definicao.

16

Funcoes Puras

Linguagens funcionais incentivam (ou obrigam) a criacao de funcoes

puras.

Ao chamar a funcao com o mesmo argumento, sempre tera a mesma

resposta.

Se nao temos efeito colateral...

• ...e o resultado de uma expressao pura nao for utilizado, nao precisa

ser computado.

• ...o programa como um todo pode ser reorganizado e otimizado.

• ...e possıvel computar expressoes em qualquer ordem (ou em

paralelo).

17

Funcoes Puras

double media (int * valores, int n) {

double soma = 0;

int i;

for (i = 0; i < n; i++)

soma_valor(&soma, valores[i]);

return soma / n;

}

void soma_valor (double * soma, int valor) {

soma += valor;

}

18

Programacao sem bugs

A ausencia de estados permite evitar muitos erros de implementacao.

O lema da linguagem Haskell: ”se compilou, o codigo esta correto!”

19

Iteracoes vs Recursoes

Em linguagens funcionais os lacos iterativos sao implementados via

recursao, geralmente levando a um codigo enxuto e declarativo.

20

Iteracoes vs Recursoes

int gcd (int m, int n) {

int r = m % n;

while(r != 0) {

m = n; n = r; r = m%n;

}

return m;

}

21

Iteracoes vs Recursoes

mdc 0 b = b

mdc a 0 = a

mdc a b = mdc b (a ‘rem‘ b)

22

Avaliacao Preguicosa

Algumas linguagens funcionais implementam o conceito de avaliacao

preguicosa.

Quando uma expressao e gerada, ela gera uma promessa de execucao.

Se e quando necessario, ela e avaliada.

23

Avaliacao Preguicosa

int main () {

int x = 2;

f(x*x, 4*x + 3);

return 0;

}

int f(int x, int y) {

return 2*x;

}

24

Avaliacao Preguicosa

int main () {

int x = 2;

f(2*2, 4*2 + 3);

return 0;

}

int f(int x, int y) {

return 2*x;

}

25

Avaliacao Preguicosa

int main () {

int x = 2;

f(4, 4*x + 3);

return 0;

}

int f(int x, int y) {

return 2*x;

}

26

Avaliacao Preguicosa

int main () {

int x = 2;

f(4, 11);

return 0;

}

int f(int x, int y) {

return 2*x;

}

27

Avaliacao Preguicosa

int main () {

int x = 2;

8;

return 0;

}

int f(int x, int y) {

return 2*x;

}

28

Avaliacao Preguicosa

f x y = 2*x

main = do

let z = 2

print (f (z*z) (4*z + 3))

29

Avaliacao Preguicosa

f x y = 2*x

main = do

let z = 2

print (2 * (z*z))

30

Avaliacao Preguicosa

f x y = 2*x

main = do

let z = 2

print (8)

A expressao 4 ∗ z + 3 nunca foi avaliada!

31

Avaliacao Preguicosa

Isso permite a criacao de listas infinitas:

[2*i | i <-[1..]]

32

Haskell

Haskell: ghc e ghci

Glasgow Haskell Compiler: compilador de codigo aberto para a linguagem

Haskell.

Possui um modo interativo ghci (similar ao iPython).

33

Glasgow Haskell Compiler

Uso recomendado de:

Git - controle de revisao Cabal - gerenciamento de projeto e

dependencias Haddock - documentacao

$ mkdir hello

$ cd hello

$ mkdir bin

$ vim Hello.hs

34

Primeiro projeto

Hello.hs:

--

-- Copyright (c) 2017 Nome - site

-- GPL version 3 or later

-- (see http://www.gnu.org/copyleft/gpl.html)

--

-- | Modulo principal

module Main where

-- |Func~ao principal

main :: IO ()

main = do

print ("Hello World")

35

Primeiro projeto

Indica que esse e o modulo principal que ira gerar o executavel.

module Main where

36

Primeiro projeto

Funcao principal e uma funcao que nao recebe parametros e retorna uma

entrada e/ou saıda de arquivos.

main :: IO ()

37

Primeiro projeto

Inıcio do programa:

main = do

38

Primeiro projeto

Instrucao de entrada/saıda de arquivos (para stdout):

print ("Hello World")

39

Primeiro projeto

$ git init

$ git add Hello.hs

$ git commit -am "ola mundo funcional!"

40

Primeiro projeto

$ ghc -o bin/Hello Hello.hs

$ ./bin/Hello

41

Primeiro projeto

Para saber mais Criando um projeto usando Cabal (projetos grandes, nao

didaticos):

https://wiki.haskell.org/How to write a Haskell program

42

Para a disciplina

Criem um repositorio no github com o nome BIGDATA2018, coloquem

todos seus codigos la!

Os codigos dados em aula estarao em:

http://github.com/folivetti/BIGDATA.

43

Tipos Basicos

Tipos de Dados

Haskell tem a tipagem forte e estatica.

Voce nao pode misturar tipos e esses devem ser bem definidos:

let x = 1 :: Integer

x + "texto" -- erro! N~ao pode somar um numero a um texto

x * 2.0 -- erro! O valor inteiro n~ao se transforma em real

x * 2 -- ok!

44

Tipos numericos

Int: inteiros de 32 ou 64 bits

Integer: Inteiros de precisao arbitraria

Float: ponto-flutuante precisao simples

Double: ponto-flutuante precisao dupla

45

Operadores

soma x y = x + y

subtrai x y = x - y

multiplica x y = x * y

divide x y = x / y

Prelude> :t (+)

(+) :: Num a => a -> a -> a

46

Operadores

soma x y = x + y

subtrai x y = x - y

multiplica x y = x * y

divide x y = x / y

Prelude> :t (/)

(/) :: Fractional a => a -> a -> a

47

Operadores

div/mod - arredonda na direcao de −∞

quot/rem - arredonda na direcao de 0

divideInt1 x y = x ‘div‘ y

divideInt1 x y = x ‘quot‘ y

resto1 x y = x ‘mod‘ y

resto2 x y = x ‘rem‘ y

48

Operadores

elevado1 x y = x ^ y

elevado2 x y = x ** y

(^) :: (Num a, Integral b) => a -> b -> a

(**) :: Floating a => a -> a -> a

49

Classes de tipos

Integral: todos os tipos inteiros

Floating: todos os tipos reais

Num: todos os numeros

50

Exemplo: razao aurea

O ghc infere o tipo de acordo com suas operacoes.

aurea = (1 + sqrt 5) / 2

51

Exercıcio

Qual o tipo dessa expressao?

aurea = (1 + sqrt 5) / 2

52

Exercıcio

Qual o tipo dessa expressao?

aurea :: Floating a => a

aurea = (1 + sqrt 5) / 2

(poderia ser Double, ne?)

53

Bool

data Bool = True | False

54

Bool

igual x y = x == y

diferente x y = x /= y

maior x y = x > y

menor x y = x < y

maiorIgual x y = x >= y

menorIgual x y = x <= y

55

Bool

True && True == True

True && False == False

True || False == True

False || False == False

not True == False

56

Ano bissexto

bissexto ano = (ano ‘rem‘ 400 == 0) || ((ano ‘rem‘ 4 == 0)

&& (ano ‘rem‘ 100 /= 0))

Na pratica podemos deixar o codigo mais enxuto...

57

Tuplas

let x = (1, "palavra")

Prelude> fst x

1

Prelude> snd x

"palavra"

58

Funcoes

Funcoes

O Haskell e baseado no Lambda Calculus em que a computacao e

baseada em funcoes de uma variavel.

Toda funcao recebe uma entrada e retorna uma saıda!

somaUm :: Integer -> Integer

somaUm x = x + 1

59

Funcoes de Uma Variavel

somaUm

Nome da funcao deve comecar com caixa baixa. O estilo padrao e o

camelCase.

60

Funcoes de Uma Variavel

somaUm ::

Definicao dos tipos de entrada e saıda.

61

Funcoes de Uma Variavel

somaUm :: Integer

Recebe um valor inteiro.

62

Funcoes de Uma Variavel

somaUm :: Integer -> Integer

Retorna um valor inteiro.

63

Funcoes de Uma Variavel

somaUm :: Integer -> Integer

somaUm x

A funcao, dado um valor x...

64

Funcoes de Uma Variavel

somaUm :: Integer -> Integer

somaUm x =

...e definida como...

65

Funcoes de Uma Variavel

somaUm :: Integer -> Integer

somaUm x = x + 1

...a expressao x + 1.

66

Mais de uma variavel

Funcoes de mais de uma variavel, na verdade sao composicoes de funcoes:

soma x y = x + y

Na verdade e:

soma x y = (soma x) (y) -- resultado de soma x aplicado em y

soma x y = \z -> x + z (y)

67

Mais de uma variavel

soma :: Integer -> (Integer -> Integer)

soma e uma funcao que recebe um inteiro e retorna uma funcao que

recebe um inteiro e retorna um inteiro.

Para facilitar removemos os parenteses e fazemos a leitura como funcoes

de multiplas variaveis.

68

”Constantes

Quando fazemos:

pi = 3.14

Nao estamos declarando uma constante, mas sim uma funcao que nao

recebe parametros e retorna sempre o valor 3.14.

69

”Constantes

Quando fazemos:

pi :: Double

pi = 3.14

Nao estamos declarando uma constante, mas sim uma funcao que nao

recebe parametros e retorna sempre o valor 3.14.

70

Assinatura

soma :: Integer -> Integer -> Integer

Essa e a assinatura da funcao, ela costuma dizer muito sobre o que ela

faz.

71

Assinatura de classes

Quando queremos criar uma funcao que recebe classes de tipos (ex.:

Num) fazemos:

soma :: Num a => a -> a -> a

Se x e y forem inteiros, a funcao retornara um inteiro; se forem Double,

ela retornara Double; se forem diferentes entre si, retornara um erro!

72

Assinatura genericas

Podemos tambem criar funcoes que recebem qualquer tipo:

f :: a -> a -> b

Funcao que recebe duas variaveis de um mesmo tipo qualquer e retorna

uma variavel de outro tipo.

Aqui a, b sao apenas nomes genericos, pode ser qualquer outra letra.

73

Exercıcio

Qual a unica funcao possıvel com a seguinte assinatura?

f :: a -> a

74

Exercıcio

Qual a unica funcao possıvel com a seguinte assinatura?

f :: a -> a

f x = x

75

Exercıcio

Crie uma funcao que receba 3 valores do tipo Double: a, b, c e retorne as

raızes da equacao:

a · x2 + b · x + c .

76

Exercıcio

Qual a assinatura dela?

77

Exercıcio

raizSegGrau :: Double -> Double -> Double -> (Double, Double)

78

Exercıcio

Escreva a funcao!

79

Exercıcio

raizSegGrau :: Double -> Double -> Double -> (Double, Double)

raizSegGrau a b c = ((-b - (sqrt (b**2 - 4*a*c)))/(2*a),

(-b + (sqrt (b**2 - 4*a*c)))/(2*a))

80

Beleza prometida

Podemos segmentar as definicoes de expressoes atraves da instrucao

where.

f x = expr

where

expr = expr1 + expr2

81

Beleza prometida

O Haskell (assim como o Python) usa espacos para definir blocos.

Lembre-se de manter seu codigo alinhado!

f x = expr

where

expr = expr1 + expr2

82

Beleza prometida

Muito melhor!

raizSegGrau :: Double -> Double -> Double -> (Double, Double)

raizSegGrau a b c = (x1, x2)

where

x1 = (-b - sqrt delta) / (2*a)

x2 = (-b + sqrt delta) / (2*a)

delta = b**2 - 4*a*c

83

Beleza prometida

Mas e se delta for negativo???

raizSegGrau :: Double -> Double -> Double -> (Double, Double)

raizSegGrau a b c = (x1, x2)

where

x1 = (-b - sqrt delta) / (2*a)

x2 = (-b + sqrt delta) / (2*a)

delta = b**2 - 4*a*c

84

Guard!!

Os guards (|) criam um desvio condicional para tratar certas

possibilidades da sua funcao:

f x

| cond1 = expr1

| cond2 = expr2

| otherwise = expr3

where

expr1 = ...

expr2 = ...

expr3 = ...

Leia: a funcao f recebe um parametro x e, se a condicao cond1 for

verdadeira, retorne expr1, se cond2 for verdadeira, retorne expr2, caso

contrario, retorne expr3.

Equivalente a um switch..case.

85

Guard!!

raizSegGrau :: Double -> Double -> Double -> (Double, Double)

raizSegGrau a b c

| delta < 0 = error "Raızes negativas!"

| otherwise = (x1, x2)

where

x1 = (-b - sqrt delta) / (2*a)

x2 = (-b + sqrt delta) / (2*a)

delta = b**2 - 4*a*c

86

Exercıcio

Reescreva a seguinte funcao utilizando os conceitos aprendidos ate entao:

bissexto ano = (ano ‘rem‘ 400 == 0) || ((ano ‘rem‘ 4 == 0)

&& (ano ‘rem‘ 100 /= 0))

87

Exercıcio

bissexto ano

| ano ‘divide‘ 400 = True

| ano ‘divide‘ 4 = not $ ano ‘divide‘ 100

where

divide x y = x ‘rem‘ y == 0

88

Pattern Matching

E possıvel tambem prever alguns casos triviais de entrada.

Considere a funcao:

mult :: Num a => a -> a -> a

mult x y = x * y

89

Pattern Matching

Sabemos que

x*1 = x

1*y = y

0*x = x*0 = 0

90

Pattern Matching

mult :: (Eq a, Num a) => a -> a -> a

mult 1 y = y

mult x 1 = x

mult 0 _ = 0

mult _ 0 = 0

mult x y = x * y

91

Pattern Matching

Classe Eq indica que sao numeros que permitem comparacao de

igualdade.

mult :: (Eq a, Num a) => a -> a -> a

mult 1 y = y

mult x 1 = x

mult 0 _ = 0

mult _ 0 = 0

mult x y = x * y

92

Pattern Matching

significa ”nao me importo com esse valor”

mult :: (Eq a, Num a) => a -> a -> a

mult 1 y = y

mult x 1 = x

mult 0 _ = 0

mult _ 0 = 0

mult x y = x * y

Os padroes sao avaliados de cima para baixo ate que um caso verdadeiro

seja encontrado.

93

Exercıcio

Use Pattern Matching para definir a funcao soma:

soma :: (Eq a, Num a) => a -> a-> a

soma x y = x + y

94

Exercıcio

Use Pattern Matching para definir a funcao soma:

soma :: (Eq a, Num a) => a -> a-> a

soma 0 y = y

soma x 0 = x

soma x y = x + y

95

Composicao de funcoes

Digamos que temos duas funcoes:

• Uma para calcular a nota numerica final

• Outra para transformar a nota em conceito

96

Composicao de funcoes

mediaFinal :: Double -> Double -> Double

mediaFinal p1 p2 = 0.4*p1 + 0.6*p2

conceito :: Double -> Char

conceito media

| media < 5 = ’F’

| media < 6 = ’D’

| media < 7 = ’C’

| media < 8 = ’B’

| otherwise = ’A’

97

Composicao de funcoes

Para compor as duas funcoes podemos definir:

a$b indica que queremos aplicar a no resultado de b.

geraConceito :: Double -> Double -> Char

geraConceito p1 p2 = conceito $ mediaFinal p1 p2

98

Composicao de funcoes

Funcoes de uma variavel podem ser compostas com o operador ”.”

nota :: Double -> Double

nota x = x*2

conceito :: Double -> Char

conceito x

| x > 5 = ’A’

| otherwise = ’F’

calcConceito = conceito . nota

99

Exercıcio

Dada a funcao aoCubo e a funcao sqrt, utilize composicao de funcoes

para calcular a expressao:

f (x) =√

(x + y)3

aoCubo :: Num a => a -> a

aoCubo x = x^3

100

Exercıcio

Dada a funcao aoCubo e a funcao sqrt, utilize composicao de funcoes

para calcular a expressao:

f (x , y) =√

(x + y)3

aoCubo :: Num a => a -> a

aoCubo x = x^3

f :: Floating a => a -> a -> a

f x y = sqrt . aoCubo $ x + y

101

Recursao

A recursividade permite expressar ideias declarativas.

Composta por um ou mais casos bases (para que ela termine) e a

chamada recursiva.

n! = n.(n − 1)!

102

Recursao

Caso base:

1! = 0! = 1

103

Recursao

Para n = 3:

3! = 3 . 2! = 3 . 2 . 1! = 3 . 2 . 1 = 6

104

Recursao

fatorial :: Integer -> Integer

fatorial 0 = 1

fatorial 1 = 1

fatorial n = n * fatorial (n-1)

105

Recursao

fatorial :: Integer -> Integer

fatorial 0 = 1

fatorial 1 = 1

fatorial n = n * fatorial (n-1)

Casos bases primeiro!!

106

Estouro de pilha

Em muitas linguagens, a chamada recursiva suspende a operacao,

armazena o estado atual em uma pilha e tenta computar a chamada

recursiva.

Isso pode levar ao estouro de pilha.

107

Estouro de pilha

No haskell ele nao armazena o estado atual na pilha, mas expande a

expressao a ser calculada.

Efetivamente ele faz: 5! = 5 * 4! = 5 * 4 * 3! ...

108

Estouro de pilha

A pilha de expressao pode estourar!

Recursao caudal tambem e util no Haskell.

109

Estouro de pilha

fatorial :: Integer -> Integer

fatorial 0 = 1

fatorial 1 = 1

fatorial n = fatorial’ n 1

where

fatorial’ 1 r = r

fatorial’ n r = fatorial’ (n-1) (n*r)

110

Multiplicacao Etıope

A multiplicacao Etıope de dois numeros m, n e dado pela seguinte regra:

• Se m for par, o resultado e a aplicacao da multiplicacao em

m/2, n ∗ 2.

• Se m for ımpar, o resultado a aplicacao da multiplicacao em

m/2, n ∗ 2 somados a n.

• Se m for igual a 1, retorne n.

111

Multiplicacao Etıope

Exemplo:

m n r

14 12 0

7 24 24

3 48 72

1 96 168

112

Multiplicacao Etıope

Implemente o algoritmo recursivo da Multiplicacao Etıope. Em seguida,

faca a versao caudal.

113

Multiplicacao Etıope

etiope :: Integer -> Integer -> Integer

etiope m n

| m==1 = n

| par m = etiope (m ‘div‘ 2) (n * 2)

| otherwise = n + (etiope (m ‘div‘ 2) (n * 2))

114

Multiplicacao Etıope

etiope :: Integer -> Integer -> Integer

etiope m n = etiope’ m n 0

where

etiope’ m n r

| m==1 = n + r

| par m = etiope (m ‘div‘ 2) (n * 2) r

| otherwise = etiope (m ‘div‘ 2) (n * 2) (r+n)

115

Listas

Listas

Definicao recursiva: ou e uma lista vazia ou um elemento do tipo

generico a concatenado com uma lista de a.

data [a] = [] | a : [a]

(:) - concatenacao de elemento com lista

116

Criando listas

lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

lista1 = [1..10] -- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

lista2 = [0,2..10] -- [0, 2, 4, 6, 8, 10]

lista3 = [0,2..] -- [0, 2, 4, 6, 8, 10,..]

117

Criando listas

-- [2..20]

lista = [ 2*x | x <- [1..10] ]

-- [0,2,4..]

lista = [ 2*x | x <- [0..] ]

lista = [ expr x | x <- lista’ ]

118

Criando listas

-- [ (1*1), (1*2), ..., (2*1), (2*2) ... ]

-- produto cartesiano

lista = [ x*y | x <- [1..10], y <- [1..10] ]

lista = [ expr x y | x <- lista1, y <- lista2 ]

119

Criando listas

-- [5, 10, 15..]

lista = [ x | x <- [1..100], x ‘mod‘ 5 == 0]

lista = [ expr x | x <- lista’, se expr’ x for verdadeiro ]

120

Criando listas

-- [1,2..]

lista = 1 : prox lista

where

prox (x:resto) = (x+1) : prox resto

(x : resto) e um pattern match que captura o primeiro elemento da lista

em x e o restante da lista em resto.

121

Criando listas

-- [1,2..]

lista = 1 : prox lista

lista = 1 : prox [1]

lista = 1 : prox (1:[])

lista = 1 : (1+1) : prox [(1+1)]

...

122

Criando listas

Fibonacci:

fib = 1 : 2 : prox fib

where

prox (x : t@(y:z)) = (x+y) : prox t

O padrao (x : t@(y : )) captura:

• x e o primeiro elemento da lista.

• t e o restante da lista (tail).

• @ faz um pattern matching em t.

• y e o segundo elemento da lista.

• z e a lista sem os dois primeiros elementos.

123

Exercıcio

No jogo FizzBuzz, todo multiplo de 3 e substituıdo por Fizz, todo

multiplo de 5 e substituıdo por Buzz e todo multiplo de 3 e 5 e

substituıdo por FizzBuss.

Crie uma lista com os elementos que nao serao substituıdos.

124

Exercıcio

notFizzBuzz = [ x | x <- [1..], x ‘rem‘ 5 /= 0, x ‘rem‘ 3 /= 0 ]

125

Operacoes com listas

Concatenacao

lista1 = [2,3,4]

lista2 = [1,2,3]

x = 1

lista3 = lista1 ++ lista2 -- [2,3,4,1,2,3]

lista4 = x : lista1 -- [1,2,34]

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

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

126

Propriedades

length :: [a] -> Int

length [] = 0

length (x:xs) = 1 + (length xs)

null :: [a] -> Bool

null [] = True

null _ = False

127

Sublistas

lista = [1..10]

take :: Int -> [a] -> [a]

take 0 xs = []

take n [] = []

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

take 2 lista -- [1,2]

128

Strings como Lista

Uma String em Haskell pode ser representada como uma lista do tipo

Char:

type String = [Char]

palavra = "Ola Mundo"

primeiraLetra :: String -> Char

primeiraLetra (w:ws) = w

primeiraLetra palavra -- ’O’

129

Trabalhando com Listas

Suponha as listas:

listaNum = [5, 3, 1, 2, 4]

listaStr = ["cachorro", "gato", "arara"]

130

Trabalhando com Listas

Digamos que queremos triplicar os numeros da primeira lista e colocar

uma exclamacao no final de cada string. Poderıamos fazer:

listaNum = [5, 3, 1, 2, 4]

listaTriplo = [3*x | x <- listaNum]

addExc (x:[]) = x : [’!’]

addExc (x:xs) = x : addExc xs

listaStr = ["cachorro", "gato", "arara"]

listaStrE = [addExc s | s <- listaStr]

131

Trabalhando com Listas

O padrao:

[f x | x <- lista]

e recorrente em muitos algoritmos, podemos criar uma funcao generica

map que sabe aplicar uma funcao f em qualquer tipo x .

132

Trabalhando com Listas

A funcao map recebe uma funcao que transforma um tipo a no tipo b,

uma lista do tipo a e retorna uma lista do tipo b. Note que a e b podem

ser iguais.

map :: (a -> b) -> [a] -> [b]

map f xs = [f x | x <- xs]

triplica x = 3*x

listaTriplo = map triplica listaNum

listaStrE = map addExc listaStr

133

Trabalhando com Listas

Digamos agora que queremos remover todos os valores ımpares de

listaTriplo:

listaTriploPar = [x | x <- listaTriplo, even x]

E tambem todos menores ou iguais a 4:

listaTriploPar = [x | x <- listaTriplo, even x, x > 4]

Esse tambem e um padrao recorrente que podemos deixar mais claro

utilizando a funcao filter.

134

Trabalhando com Listas

A funcao filter, recebe uma funcao que avalia um tipo a em um

booleano, uma lista do tipo a e retorna uma lista do mesmo tipo, filtrada

pelo predicado.

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

filter f xs = [x | x <- xs, f x]

maiorQuatro x = x > 4

listaFinal = filter maiorQuatro

$ filter even

$ map triplica listaNum

Com isso conseguimos representar as operacoes como um fluxo de dados,

os elementos de listaNum passa pela funcao triplica para depois passar

pelo crivo de even e, finalmente, por maiorQuatro.

135

Funcoes anonimas

Quando as funcoes a serem passadas como parametro sao pequenas,

podemos utilizar as funcoes anonimas:

map (\x -> 3*x) listaNum

filter \x -> x > 4) listaTriplo

A sintaxe \ representa o sımbolo λ, entao devemos ler como funcao λ que

recebe um parametro x e retorna (→) uma expressao em funcao de x .

Note que as funcoes anonimas podem ter apenas uma unica expressao

simples.

136

Pointfree notation

Adicionalmente, quando a funcao ja esta definida ou e um operador

podemos utilizar o estilo pointfree:

map (3*) listaNum

map (*3) listaNum

filter (>4) listaTriplo

137

Exercıcio

Dada a seguinte funcao:

collatz :: Integer -> Integer

collatz n

| even n = n ‘div‘ 2

| otherwise = 3*n + 1

Gere uma lista com os elementos ımpares da aplicacao dessa funcao nos

numeros naturais.

138

Exercıcio

Dada a seguinte funcao:

collatz :: Integer -> Integer

collatz n

| even n = n ‘div‘ 2

| otherwise = 3*n + 1

lista = filter odd $ map collatz [1..]

Gere uma lista com os elementos ımpares da aplicacao dessa funcao nos

numeros naturais.

139

Trabalhando com Listas

Agora queremos somar os valores de listaFinal, para isso podemos criar

uma funcao:

sum :: Num a => [a] -> a

140

Trabalhando com Listas

Agora queremos somar os valores de qtdeBrinquedos, para isso podemos

criar uma funcao:

sum :: Num a => [a] -> a

sum [] = 0

sum (x:xs) = x + (sum xs)

total = sum listaFinal

141

Trabalhando com Listas

A funcao sum pode ser generalizada para qualquer tipo de operacao.

Digamos que queremos calcular a produtoria de uma lista. Como

podemos alterar sum?

sum :: Num a => [a] -> a

sum [] = 0

sum (x:xs) = x + (sum xs)

prod :: Num a => [a] -> a

142

Trabalhando com Listas

A funcao sum pode ser generalizada para qualquer tipo de operacao.

Digamos que queremos calcular a produtoria de uma lista. Como

podemos alterar sum?

sum :: Num a => [a] -> a

sum [] = 0

sum (x:xs) = x + (sum xs)

prod :: Num a => [a] -> a

prod [] = 1

pro (x:xs) = x * (prod xs)

Basicamente alteramos o valor neutro de 0 parar 1 e o operador aplicado

no caso generico.

143

Folding

Esse tipo de operacao e conhecido como fold ou reduce dependendo da

linguagem funcional. Basicamente essa operacao parte de um elemento

neutro e aplica uma operacao de reducao sequencialmente entre o

acumulador atual e o proximo elemento da lista:

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

foldr f z [] = z

foldr f z (x:xs) = f x (foldr f z xs)

Uma funcao que agrega o resultado de um tipo a com um tipo b, um

elemento neutro a e uma lista de b, resultando em a.

144

Folding

Alternativamente podemos definir uma versao caudal:

foldl :: (a -> b -> a) -> a -> [b] -> a

foldl f z [] = z

foldl f z (x:xs) = foldl f (f z x) xs

145

Folding

Nossas funcoes soma e produtoria podem ser definidas como:

sum = foldl (+) 0

produtoria = foldl (*) 1

146

Exercıcio

Dada a definicao do operador &&:

(&&) False _ = False

(&&) _ False = False

(&&) _ _ = True

Expanda as seguintes expressoes:

foldl (&&) False [False, False, False, False]

foldr (&&) False [False, False, False, False]

147

Exercıcio

foldl (&&) (False && False) [False, False, False]

foldl (&&) ((False && False) && False) [False, False]

foldl (&&) (((False && False) && False) && False) [False]

foldl (&&) (((False && False) && False) && False) && False

(((False && False) && False) && False) && False

False

148

Exercıcio

False && (foldr (&&) False [False, False, False])

False

149

Trabalhando com Listas

Finalmente, temos as funcoes sort e nub que ordena uma lista e gera

apenas os elementos unicos, respectivamente:

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

nub :: Eq a => [a] -> [a]

150

Algebraic Data Type

Algebraic Data Types

• Tipos compostos de dados primitivos.

• Permite expressividade.

151

Tipos de Dados Algebricos

Tipo soma:

data Bool = True | False

• data: declara que e um novo tipo

• Bool: nome do tipo

• True — False: poder assumir ou True ou False

152

Tipos de Dados Algebricos

Tipo produto:

data Ponto = Ponto Double Double

• data: declara que e um novo tipo

• Ponto: nome do tipo

• Ponto: construtor (ou envelope)

• Double Double: tipos que ele encapsula

153

Tipos de Dados Algebricos

Para ser possıvel imprimir esse tipo:

data Ponto = Ponto Double Double

deriving (Show)

• deriving: derivado de outra classe

• Show: tipo imprimıvel

Isso faz com que o Haskell crie automaticamente uma instancia da

funcao show para esse tipo de dado.

154

Convertendo entre tipos e String

A funcao read le uma String e converte para um tipo especıfico, a funcao

show converte um tipo para uma String.

x = read "2" :: Integer

y = read "2.3" :: Double

z = read "True" :: Bool

sx = show x

sy = show y

sz = show z

155

Tipos de Classe - deriving

Alem do Show temos os seguintes tipos de classe que podem ser

derivadas:

• Read: que podem ser lidas a partir de uma String

• Eq: que possuem relacao de igualdade

• Ord: possuem relacao de ordem

• Enum: que sao enumeraveis

• Bounded: possuem um limite inferior e superior

Caso o compilador nao consiga criar as funcoes pertinentes

automaticamente, sera necessario implementa-las.

156

Tipos de Classe - deriving

Outras duas funcoes importantes para conversao:

• fromIntegral: converte de um inteiro para um tipo da classe Num,

pertinente para a expressao a ser calculada

• fromEnum: converte para a posicao de um tipo enumeravel.

157

Tipos de Dados Algebricos

data Ponto = Ponto Double Double

deriving (Show)

main = do

print (Ponto 12.2 1.1)

Ponto 12.2 1.1

158

Tipos de Dados Algebricos

data Ponto = Ponto Double Double

deriving (Show)

dist :: Ponto -> Ponto -> Double

dist (Ponto x1 y1) (Ponto x2 y2) = sqrt $ (x1-x2)^2 + (y1-y2)^2

159

Tipos de Dados Algebricos

data Circ = Circ Ponto Double

deriving (Show)

Quero mais declarativo!

160

Tipos de Dados Algebricos

type Centro = Ponto

type Raio = Double

data Circ = Circ Centro Raio

deriving (Show)

type e um apelido para um tipo!

161

Tipos de Dados Algebricos

data Dias = Seg | Ter | Qua | Qui | Sex | Sab | Dom

deriving (Show, Enum)

Enum e enumerativo:

succ Seg == Ter

pred Ter == Seg

162

Tipos de Dados Algebricos

data Dias = Seg | Ter | Qua | Qui | Sex | Sab | Dom

deriving (Show, Enum, Eq)

Eq e comparativo de igualdade:

Seg == Ter -- False

Seg == Seg -- True

163

Tipos de Dados Algebricos

data Dias = Seg | Ter | Qua | Qui | Sex | Sab | Dom

deriving (Show, Enum, Ord)

Ord e relacional:

Seg < Ter -- True

Ter > Seg -- False

164

Tipos de Dados Algebricos

data List a = Nil | Cons a (List a)

Uma lista de tipo a e definido ou como vazio (Nil) ou como a constante

do tipo a e uma lista do tipo a.

[1,2,3] = Cons 1 (Cons 2 (Cons 3 Nil))

165

Tipos de Dados Algebricos

data Maybe a = Nothing | Just a

166

Tipos de Dados Algebricos

safeDiv :: Double -> Double -> Maybe Double

safeDiv x y

| y==0 = Nothing

| otherwise = Just (x / y)

media l = case safeDiv (sum l) (fromIntegral $ length l) of

Nothing -> 1/0 -- infinity

Just m -> m

167

Atividade 01

Faca os exercıcios das paginas Exercıcios Basicos, Exercıcios sobre

Funcoes e Exercıcios sobre Listas da seguinte pagina:

Curso de Haskell

168