Programação Funcional Padrões Simples e Definição de Operadores em Haskell 3a. Seção de Slides Hoje: • Alguns comentários e respostas das aulas passadas • Operadores, associatividade • Padrões e casamentos • Tipos Bool, Carácteres e String e Float 2 Obs. do Lab #2 Seja calcular: 7^0 + 7^1 + ... + 7^n Logo uma função de 7^n é dada por: pot_7 0 = 1 pot_7 n = 7 * pot_7 (n-1) Aplicar uma recursão sobre: 7^0 + 7^1 + ... + 7^n Então: serie_7 serie_7 pot_7 pot_7 0 = pot_7 n = pot_7 0 n + serie_7 pot_7 (n-1) Aula02> pot_7 2 49 Aula02> serie_7 pot_7 2 57 Aula02> :type serie_7 serie_7 :: (Num a, Num b) => (b -> a) -> b -> a 3 Correções na Apostila --Correção na apostila do Michell -- função ... SOMA ESPECIAL s_michell f 1 = 1 s_michell f n = f n + s_michell f (n-1) (página 11, no final) f x | x == 0 = 10 | x < 0 = abs (x) | x > 0 = x - f (x-1) Faltava também definir f .... 4 Perguntas: write :: String -> String write x = x Main> "xxxx Main> ERROR write "xxxx yyyy" yyyy" write xxxx yyyy - Undefined variable "yyyy" Main> 5 Um pouco sobre entradas/saídas: .... putStrLn “Alo mundo” .... nome_string <- getLine -- Não usa o igual... pois não é uma -- função .... 6 Comentários: ou_excl False False = ou_excl True True = ou_excl _ _ = False False True Main> ou_excl True False True Main> ou_excl (4 > 6) (3 < 8) True Obs. No uso de parênteses.... são necessários 7 Operadores • Como visto: a linguagem Haskell contém vários operadores, como: +, -, *, /, `div`, `mod`, ^, etc. • Operadores são infixos, o que significa que são escritos entre os seus argumentos. • Parênteses podem ser usados em qualquer operação: (((4+8)*3)+2) • Propriedades que auxiliam na eliminação de parênteses: associatividade e prioridade de operadores. 8 Associatividade • Uma operação op é associativa se (x op y) op z = x op (y op z) • Operações associativas dispensam parênteses. • Ou seja, não interessa a ordem de como os cálculos são feitos. • Uma operação não associativa é classificada como: – associativa à esquerda; ou – associativa à direita. 9 Associatividade Main> 7 Main> 1 Main> 3 Main> 1 4 + 2 + 1 (4 - 2) - 1 Adição (+) é associativa. 4 - (2 - 1) 4 - 2 - 1 Subtração (-) é associativa à esquerda. Prelude> 9-(7+2) Subtração (-) não 0 Prelude> é (9-7)+2 associativa. 4 Finalmente, na Prelude> 9-7+2 mistura, a associativa 4 à esquerda 10 Precedência de operadores • A associatividade define a ordem de computação de expressões envolvendo mais de uma ocorrência de um mesmo operador. Ex: 2^3^2 Prelude> 2^3^2 512 Exponenciação é Prelude> 2^9 associativa à direita. 512 • E quando operadores diferentes são utilizados? • Por exemplo: 2+3*4 = 14 3^4*2 = 162 • Alguns operadores têm precedência maior do que outros. 11 Precedência de operadores Main> 2 + 3 * 4 14 Main> 2 ^ 3 * 4 32 Operador * tem precedência 7, enquanto + tem prioridade 6. Operador ^ tem precedência 8. 12 Precedência de operadores Main> 2 + 3 * 4 14 Main> 2 ^ 3 * 4 32 Main> fat 4 + 1 ??????????????????? 1 2 3 4 fat fat | | :: Int -> Int n n <= 0 = 1 otherwise = n * fat (n-1) 13 Precedência de operadores Main> 2 + 3 * 4 14 Main> 2 ^ 3 * 4 32 Main> fat 4 + 1 25 Aplicação de função tem precedência máxima. Tabela de associatividade e precedências pode ser encontrada no Apêndice E do livro-texto. 1 2 3 4 fat fat | | :: Int -> Int n n <= 0 = 1 otherwise = n * fat (n-1) 14 Precedência de operadores Main> fat (-1) 1 1 2 3 4 fat fat | | :: Int -> Int n n <= 0 = 1 otherwise = n * fat (n-1) 15 Precedência de operadores Main> fat (-1) 0 Main> fat -1 ERROR - Illegal class constraint in inferred type 1 2 3 4 fat fat | | :: Int -> Int n n <= 0 = 1 otherwise = n * fat (n-1) 16 Definindo Operadores • Haskell permite a definição de novos operadores. • Os nomes dos operadores são formados por: ! # $ % & * + . / < = > ? @ \ ^ | : - ~ • Restrições: – nomes não podem começar por ":" operador Define como operador Define não como operador – "-" e Define "~" só podem ser o primeiro símbolo do nome como associativo. associativo àassociativo esquerda. à direita. – símbolos (e combinações) reservados: :: => = @ \ | ^ <- -> • Mudar associatividade ou precedência: infix infixl infixr 17 Definindo Operadores 1 infixl 7 &&& Define operador &&& como associativo à esquerda, com predência 7. 18 Definindo Operadores 1 2 3 4 5 6 infixl 7 &&& (&&&) :: Int -> Int -> Int a &&& b | a > b = a | otherwise = b Main> 10 &&& 20 20 19 Definições com Padrões • As definições de funções vistas até agora consistem de uma única equação. • É possível definir uma função usando várias equações. • As equações devem especificar como a função se comporta quando aplicada a determinados padrões. • Os padrões mais simples são variáveis e constantes. 20 Definições com Padrões • Versão anterior - uma equação condicional: 1 2 3 4 totalSales :: Int -> Int totalSales n | n == 0 = sales 0 | otherwise = totalSales (n-1) + (sales n) • Nova versão - usando padrões (“pattern matching”): 1 .totalSales 0 = sales 0 2 .totalSales n = totalSales (n-1) + (sales n) 21 Definições com Padrões 1 .totalSales 0 = sales 0 2 .totalSales n = totalSales (n-1) + (sales n) • A primeira equação é aplicada somente ao valor 0. • A segunda se aplica a todos os outros casos. • Regra geral: utilizar a primeira equação que "casa" com os argumentos, na ordem especificada. • Um argumento a casa com um padrão p se: – p é uma constante e a é igual a p ; ou – p é uma variável. 22 Definições com Padrões • O padrão "_" casa com qualquer argumento (“Prolog like”). • É usado quando o valor do argumento não é necessário no lado direito da equação. 1 .is Zero :: Int -> Bool 2 .isZero 0 = True 3 .isZero _ = False 23 Definições com Padrões • Exemplo envolvendo padrões e equação condicional: 1 2 3 4 5 6 7 .fib . .fib .fib .fib . | . | :: Int -> Int 0 = 0 1 = 1 n n > 1 otherwise = fib (n-1) + fib (n-2) = 0 24 Programando com Booleanos • Os valores booleanos são representados pelas constantes False e True, do tipo Bool. • Operadores: && ("e" lógico) || ("ou" lógico) not (negação) • Semântica dos operadores: t1 T T F F t2 T F T F t1 && t2 T F F F t1 || t2 T T T F t1 T F not t1 F T 25 Programando com Booleanos • Uma definição para "not": 1 .meuNot :: Bool -> Bool 2 .meuNot True = False 3 .meuNot False = True 26 Programando com Booleanos • "ou exclusivo" - retorna True se exatamente um dos argumentos for True: exOr :: Bool -> Bool -> Bool exOr x y = (x || y) && not (x && y) (Veio da definição do Ou-exclusivo... Lógica) • Definição usando padrões: 1 .exOr False x = x 2 .exOr True x = not x 27 Programando com Booleanos • Construir uma função isZeroDay que, dado o número de um dia, retorne: – True, se o número de unidades vendidas (função sales) no dia é zero; – False, caso contrário. 1 2 3 4 .isZeroDay :: Int -> Bool .isZeroDay n . | sales n == 0 = True . | otherwise = False 28 Programando com Booleanos 1 2 3 4 .isZeroDay :: Int -> Bool .isZeroDay n . | sales n == 0 = True . | otherwise = False • Quando a expressão sales n == 0 tem valor True, a função retorna True. • Quando a expressão sales n == 0 tem valor False, a função retorna False. 29 Programando com Booleanos 1 2 3 4 .isZeroDay :: Int -> Bool .isZeroDay n . | sales n == 0 = True . | otherwise = False Esta função será reusada em seguida.... • Simplificação: 1 .isZeroDay :: Int -> Bool 2 .isZeroDay n = (sales n == 0) Ao invés do True ou False... A própria expressão! Humm bem elegante... 30 Programando com Booleanos • Construir uma função zeroInPeriod que, dado o número de um dia, retorne: – True, se não foi vendida nenhuma unidade do produto em algum dia do período 0,...,n; – False, caso contrário. 1 2 3 4 .zeroInPeriod :: Int -> Bool . .zeroInPeriod 0 = isZeroDay 0 .zeroInPeriod n = ???????????????? 31 Programando com Booleanos 1 2 3 4 .zeroInPeriod :: Int -> Bool . .zeroInPeriod 0 = isZeroDay 0 .zeroInPeriod n = sales 0 sales 1 ... sales (n-1) zeroInPeriod (n-1) sales n isZeroDay n 32 Programando com Booleanos 1 2 3 4 .zeroInPeriod :: Int -> Bool . .zeroInPeriod 0 = isZeroDay 0 .zeroInPeriod n = zeroInPeriod (n-1) || isZeroDay n sales 0 sales 1 ... sales (n-1) zeroInPeriod = zeroInPeriod (n-1) || sales n isZeroDay n 33 Caracteres e Strings • Char : tipo de Haskell associado a caracteres. • Caracteres individuais são inseridos em aspas simples. Exemplos: ´d´, ´3´. • Caracteres especiais: ´\t´ ´\n´ ´\\´ ´\´´ ´\"´ ´\97´ tab new line barra invertida aspa simples aspa dupla caractere ´a´ 34 Caracteres e Strings • Haskell utiliza a codificação padrão ASCII para caracteres. • As funções chr e ord são usadas para conversões. Main> chr 97 ´a´ Main> ord ´A´ 65 35 Caracteres e Strings • Conversão de letra minúscula para maiúscula: 1 2 3 4 .offset = ord ´A´ - ord ´a´ . .capitalize :: Char -> Char .capitalize ch = chr (ord ch + offset) • Verificar se um caractere é um dígito: 1 .isDigit :: Char -> Bool 2 .isDigit ch = (´0´<= ch) && (ch <= ´9´) 36 Caracteres e Strings • Strings de caracteres pertencem ao tipo String. • São inseridas entre aspas duplas. Exemplos: "abcdef" "uma linha\noutra linha" "" "O caractere \´a\´ : \97" • Strings são concatenadas usando o operador ++ • Ex: “alo, ” ++ “ bom “ ++ “ dia “ 37 Caracteres e Strings • Haskell permite que novos tipos sejam criados, usando a palavra-chave type. • O tipo String é, na realidade, uma lista de caracteres: type String = [Char] • As operações que manipulam listas (duas aulas a frente) podem ser usadas também para strings. • Como no Prolog: “[“ .... “]” (colchetes)... são usados para especificar uma lista, no caso: “uma string é uma lista de carácteres” 38 Números de Ponto Flutuante • Números de ponto flutuante são representados, em Haskell, pelos tipos Float e Double. Exemplos: 3.141592 -1.2345 987.654 • Haskell usa também notação científica: 2 9.87654e02 = 9.87654*10 = 987.654 -4 31415.92e-4 = 31415.92*10 = 3.141592 39 Números de Ponto Flutuante • Haskell oferece uma série de funções que atuam sobre números de ponto flutuante. Main> sin (pi/2) 1.0 Main> 1.1 ^ 10 2.59374 Main> 1.1 ^ 10 2.59374 Main> 1.1 ** 10.0 2.59374 Main> 2.5**(3.5) 24.7053 Main> sqrt 2 1.41421 40 Números de Ponto Flutuante Main> round 2.49 2 Main> ceiling 2.49 3 Main> floor 2.49 2 Main> log 2 0.693147 main> logBase 2 16 4.0 41