Programação Funcional Tuplas e Definição de Funções Locais em Haskell 4a. Seção de Slides Novidades da disciplina: • Site da disciplina: http://geocities.yahoo.com.br/lpg3udesc/ • Email: [email protected] • Márcio Ferreira da Silva 2 Hoje: Sejam as construções abaixo: sxy x y z = x + y + z sXYZ ( x , y, z ) = x + y + z Qual a diferença entre elas ? Main> sxyz 3 4 0 7 Main> sXYZ ( 3 , 4, 0 ) 7 3 Dicas import Observe sumi :: Int -> Int sumi n | n <= 0 =0 | otherwise = n + (observe "sumi" (n-1)) 4 Tuplas • A linguagem Haskell permite a definição de tipos compostos, chamados de tuplas. • Tuplas são construídas a partir de tipos mais simples. • O tipo (t1,t2,...tn) consiste de tuplas com valores (v1,v2,...vn) onde v1::t1, v2::t2, vn::tn. • Enfim, fazer a composição de tipos de dados ... 5 Tuplas • Definições de novos tipos podem ser introduzidas pelo comando type. • Exemplo: informações sobre uma pessoa, representadas pelo nome, telefone e idade. 1 .type Pessoa = (String, String, Int) 2 .maria :: Pessoa 3 .maria = ("Maria", "32162724", 56) 6 Tuplas • Tuplas são usadas quando é necessário agrupar dados, com algum sentido entre eles. • Exemplo: Uma pessoa tem uma idade, um sexo, um nome, etc • Outra utilidade: uma função que pode retornar mais de um valor como resposta, pois está tudo encapsulado! 7 Exemplo: Exemplo: Uma função que retorne o mínimo (menor valor) e também o máximo de 3 valores inteiros fornecidos: menor3maior :: Int -> Int -> Int-> (Int,Int) menor3maior x y z = ( menor (menor x y) z , maior (maior x y) z) Aula04> menor3maior (-6,5) (-4) 5 (-6) 8 Funções Auxiliares maior :: Int -> Int -> Int maior x y = if x >= y then x else y menor :: Int -> Int -> Int menor x y = if x <= y then x else y 9 Tuplas • Padrões podem ser utilizados na definição de funções sobre tuplas. • Em vez de usar uma variável x para um argumento de tipo (Int, Int), por exemplo, pode-se usar um padrão como (x,y). 1 .addPair :: (Int,Int) -> Int 2 .addPair (x,y) = x + y 10 Tuplas 1 .addPair :: (Int,Int) -> Int 2 .addPair (x,y) = x + y addPair (34, 32) = 34 casa com x e 32 casa com y. 11 Tuplas 1 .addPair :: (Int,Int) -> Int 2 .addPair (x,y) = x + y addPair (34, 32) = 34 + 32 = 66 12 Tuplas • Importante: as duas definições seguintes são diferentes! 1 2 3 4 5 .addPair :: (Int,Int) -> Int .addPair (x,y) = x + y . .addTwo :: Int -> Int -> Int .addTwo a b = a + b Main> addPair (34,32) 66 Main> addTwo 34 32 66 Visto no início da aula 13 Tuplas • Padrões podem conter padrões aninhados. • Isto é muito útil... 1 .shift :: ((Int,Int),Int) -> (Int,(Int,Int)) 2 .shift ((a,b),c) = (a,(b,c)) Main> shift ((1,2),3) (1,(2,3)) 14 Tuplas • As funções que extraem partes de uma tupla podem ser especificadas usando casamento de padrões. 1 2 3 4 5 .type Pessoa = (String, String, Int) . .nome :: Pessoa -> String .fone :: Pessoa -> String .idade :: Pessoa -> Int 15 Tuplas • Funções que extraem partes de uma tupla podem ser especificadas usando casamento de padrões. 1 2 3 4 5 6 7 8 9 .type Pessoa = (String, String, Int) . .nome :: Pessoa -> String .fone :: Pessoa -> String .idade :: Pessoa -> Int . .nome (n,f,i) = n .fone (n,f,i) = f .idade (n,f,i) = i 16 1 .type Pessoa = (String, String, Int) 2 . Observe que maria 3 .nome :: Pessoa -> String 4 .fone :: Pessoa -> String uma função 5 .idade :: Pessoa -> Int 6 .nome (n,f,i) = n 7 .fone (n,f,i) = f 8 .idade (n,f,i) = i 9 . 10 . maria :: Pessoa 11 . maria = ("Maria Antonia", "32162724", 56) Main> nome maria "Maria Antonia" Main> idade maria 56 é E nome também é uma função, cujo argumento é uma função definida por uma tupla 17 Tuplas • Haskell possui duas funções de extração prédefinidas para tuplas de 2 elementos (pares). 1 .fst (x,y) = x 2 .snd (x,y) = y Main> fst (1,2) 1 Main> snd (1,2) 2 18 Definições de Funções • Por enquanto, as definições de funções estudadas são da forma: fun p1 p2 ... pn | g1 | g2 ... | otherwise = e1 = e2 = er 19 Definições Locais • Cada equação pode também ser seguida por uma lista de definições locais. • Essas definições são escritas após a palavra-chave where. 1 2 3 4 5 6 .sumSquares :: Int -> Int -> Int .sumSquares m n . = sqM + sqN . where . sqM = m * m . sqN = n * n 20 Definições Locais • Formato geral: fun p1 p2 ... pn | g1 = e1 ... | otherwise = er where v1 = r1 v2 a1...ak = r2 ... Indentação é importante. Define o conceito de “Bloco” Definições locais podem incluir funções! 21 1 2 3 4 5 6 .sumSquares :: Int -> Int -> Int .sumSquares m n . = sqM + sqN . where . sqM = m * m . sqN = n * n • Exemplo com definição de função local: 1 2 3 4 5 .sumSquares .sumSquares . = sq m + . where . sq x = :: Int -> Int -> Int m n sq n x * x sq é local... Visível apenas em sumSquares 22 Definições Locais • É possível fazer também definições locais a expressões, usando a palavra-chave let. Main> let x = 5 in x^2 + 2*x - 4 31 • Se forem usadas 2 ou mais definições, devem ser separadas por ";" . Main> let x = 5; y = 4 in x^2 + 2*x - y 31 23 Regras de Escopo • O escopo de uma definição é a parte do programa onde ela é visível. • As definições no primeiro nível de indentação de um script haskell são visíveis em todo o programa. • A ordem de definição não importa. 1 2 3 4 5 6 .isOdd, isEven :: Int -> Bool . .isOdd 0 = False .isOdd n = isEven (n-1) .isEven 0 = True .isEven n = isOdd (n-1) 24 Regras de Escopo • Definições locais (cláusulas where) têm escopo mais reduzido, assim como as variáveis na definição de uma função. Definições locais podem 1 .maxsq x y ser usadas antes de serem 2 . | sqx > sqy = sqx definidas. 3 . | otherwise = sqy 4 5 6 7 8 . . . . . where sqx = sq x sqy = sq y sq :: Int -> Int sq z = z * z Declaração do tipo pode ser feita localmente. 25 Regras de Escopo • Definições locais (cláusulas wherede ) têm mais Escopo x, yescopo , reduzido, assim como as variáveis nasqdefinição de sqx, sqy e . uma função. 1 2 3 4 5 6 7 8 .maxsq x y . | sqx > sqy = sqx . | otherwise = sqy . where . sqx = sq x . sqy = sq y . sq :: Int -> Int . sq z = z * z Escopo de z. 26 Regras de Escopo • Se um mesmo nome é utilizado mais de uma vez, vale a definição mais local. 1 2 3 4 5 6 7 8 .maxsq x y . | sqx > sqy = sqx . | otherwise = sqy . where . sqx = sq x . sqy = sq y . sq :: Int -> Int . sq x = x * x Nome "x" é redefinido localmente. 27 Exemplos • Problema: construir uma função max3oc :: Int -> Int -> Int -> (Int,Int) que retorna o máximo (maior) de 3 inteiros, e também o número de vezes que esse maior valor ocorre entre os 3. • Exemplo de execução: Main> max3oc 10 30 20 (30,1) Main> max3oc 15 12 15 (15,2) 28 Exemplos • Solução natural: achar o máximo/maior e contar quantas vezes ocorre entre os 3 valores. 1 2 3 4 5 6 7 .max3oc :: Int -> Int -> Int -> (Int,Int) . .max3oc x y z . = (max, igCont) . where . max = maxi3 x y z . igCont = iguais3 max x y z 29 Exemplos • Para achar o máximo entre 3 valores, pode-se usar a função que encontra o máximo entre 2 valores. 1 2 3 4 5 6 7 .maxi :: Int -> Int -> Int .maxi x y . | x >= y = x . | otherwise = y . .maxi3 :: Int -> Int -> Int -> Int .maxi3 x y z = maxi x (maxi y z) 30 Exemplos • Voltando ao problema inicial... 1 2 3 4 5 .max3oc x y z . = (max, igCont) . where . max = maxi3 x y z . igCont = iguais3 max x y z • Como definir iguais3 :: Int -> Int -> Int -> Int -> Int ??? 31 Exemplos • Uma solução: 1 2 3 4 5 6 .iguais3 v a . = va + vb . where . va = if . vb = if . vc = if b c + vc a==v then 1 else 0 b==v then 1 else 0 c==v then 1 else 0 • OBS: if c then e1 else e2 retorna e1 se c for True, e retorna e2, se c for False. 32 Exemplos • A definição pode ser melhorada, eliminando a repetição de 3 comparações análogas: 1 2 3 4 5 6 .iguais3 v a . = va + vb . where . va = if . vb = if . vc = if b c + vc a==v then 1 else 0 b==v then 1 else 0 c==v then 1 else 0 33 Exemplos • A definição pode ser melhorada, eliminando a repetição de 3 comparações análogas: 1 2 3 4 5 .iguais3 v a b c . = igv a + igv b + igv c . where . igv :: Int -> Int . igv x = if x==v then 1 else 0 O valor ”v" é maior já encontrado 34 Mais Exemplos .. Um sistema de menu’s.... main = imp_menu imp_menu = do putStrLn putStrLn putStrLn putStrLn putStrLn putStr le_opcao Criando um executável... Tudo começa em main "\n ********************" " Opção i: Incluir " " Opção e: Excluir " " Opção s: Sair " " ********************" " Digite i, e ou s: " 35 Continuando .. le_opcao = do opcao <- getChar f_menu opcao f_menu i = do case (i) 'i' -> 'e' -> 's' -> _ -> of putStrLn "\n Chamou a funcao 1 !!!" putStrLn "\n Chamou a funcao 2 !!!" putStrLn "\n Chamou a funcao 3 !!!" imp_menu 36 Finalizando ... • Temos palavras reservadas como: do, case, let, where, in, if, then, else, of, etc, a serem utilizadas nas funções; • Diríamos que é a parte sequencial das linguagens funcionais, que ainda nos prende as Máquinas de Von Neumann (sequenciais); • Contudo, o uso delas pode ser contornado (se quiser, opcional), usando um estilo 100% funcional de se programar; • Estas seguem regras de escopo; • Estas seguem uma identação de blocos. Cuidar... 37