Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Aula 02 -> Pattern Matching
Olá Pessoal!
Aula passada estavamos falando em listas, agora vamos aprender a usar as listas (e outras coisas) nas nossas funções.
Haskell, assim como algumas outras linguagens funcionais, possui um mecanismo chamado 'pattern matching', do inglês 'concordância de padrões',
e sem esse cara seria um trabalho imenso programar em Haskell.
Como funciona?
É a coisa mais fácil do mundo!
Vamos definir a função que calcula o fatorial de um número:
fatorial :: Int -> Int
fatorial n = if n == 1
then 1
else n * (fatorial (n-1))
Fantástico, mas esse 'if' só está ocupando espaço, isso sim!
Esse tipo de condição podemos impor diretamente na declaração dos parametros da fução, olhem:
fatorial :: Int -> Int
fatorial 1 = 1
fatorial n = n * (fatorial (n-1))
Agora temos uma função por ramos, e o Haskell escolhe sozinho qual o ramo apropriado a utilizar.
Existem ainda alguns operadores que podem ser usados nos parametros, como é o caso do '+'.
fatorial :: Int -> Int
fatorial 1 = 1
fatorial (n+1) = (n+1) * (fatorial n)
Um outro exemplo interessante é a função 'e lógico', que é definida para bool's, por exemplo:
and :: Bool -> Bool -> Bool
and True True = True
and True False = False
and False True = False
and False False = False
Notem que em apenas um ramo da função retornamos True, de resto retornamos SEMPRE False.
Isso pode ser escrito usando um underline, '_', por exemplo:
and :: Bool -> Bool -> Bool
and True True = True
and _ _ = False
E esse underline pode ser lido como 'qualquer coisa'.Essa função é interpretada como: "Retorna 'True' se o input for 'True True', se for qualquer outra coisa retorna 'False'"
Atenção!!!
Se definissemos a função da seguinte forma:
and :: Bool -> Bool -> Bool
and _ _ = False
and True True = True
Estaria completamente errado!O Haskell checa os ramos na ordem em que foram definidos, e executa o primeiro que satisfizer o padrão.
Nesse caso, o primeiro ramo satisfaz QUALQUER padrão, portanto é sempre executado.
nota
Agora que estamos falando em pattern matching, vou vos mostrar mais um tipo de dado em Haskell.
Os n-úplos.
Que não são nada mais do que os pares, ternos, quadruplos, quintuplos, etc...
Para declarar um par de inteiros fazemos:
(Int, Int) Para declarar um par de um float e um par de inteiros fazemos:
(Float, (Int, Int)) Se temos dois inteiros, por exemplo 5 e 7, e queremos colocálos num par é mesmo MUITO fácil:
(5, 7) É só escrever os pareteses e separá-los com uma vírgula.
Os pares em Haskell podem ser dissecados com o pattern matching,
Vamos imaginar, por exemplo, uma função que recebe um par e retorna um Bool.
Nesse par temos uma lista de números e um número.
funcao :: ([Int], Int) -> Bool
funcao par = .....
E teríamos que utilizar as funções fst, e snd para pegar o primeiro e o segundo elemento do par, respectivamente.Mas, o pattern matching nos salva!
funcao :: ([Int], Int) -> Bool
funcao (lista, n) = ......
E notem que o haskell separou os items do par para nós.Onde 'lista' é do tipo [int] e 'n' é do tipo Int.
Falando em Haskell, 'lista :: [int]' e 'n :: Int'
As listas também podem ser dissecadas.
Lembram-se do operador ':'??
O ':' insere um elemento no início de uma lista, logo, a sua assinatura é:
(:) :: a -> [a] -> [a]E ele pode ser usado no pattern matching.Quando recebemos uma lista, podemos dividir ela no primeiro elemento e o resto da lisa, por exemplo:
funcao2 :: [Int] -> Int
funcao2 (h:t) = ....
Onde h é o primeiro elemento, e 'h :: Int', e t é o resto da lista, 't :: [int]'
Podemos também colocar outro ramo, definindo o funcionamento quando a lista é vazia:
funcao2 :: [Int] -> Int
funcao2 [] = .....
funcao2 (h:t) = ....
Bom, por hoje é só!
Detalhe: Notei que ninguém respondeu o tópico da última aula, o tópico está aqui para tirar dúvidas.
Se não entenderam algo, por favor falem!
=D
Alguns exercícios:
//(a) qual o tipo de 'h' e 't'?
fA :: [Int] -> (Bool, Int)
fA (h:t) = ....
//(B) qual o tipo de 'n', 'x', 'y', e 'z'?
fB :: (Int, Int, Float) -> [Bool] -> Int
fB (x, y, z) n = .....
//(c) qual o tipo de 'p' e 'ps'?
fC :: [(Int, Int)] -> Bool
fC (p:ps) = ....
//(d) qual o tipo de 'p1', 'p2', e 'resto' ??
fD :: [(Int, Float)] -> Bool
fD ((p1, p2):resto) = .....
//(e) qual o tipo de 'x', 'xs' e 'y' ???
fE :: [a] -> [b] -> [(a, B)]
fE (x:xs) y = ....