Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original
Notas de Aula - Programação I Tipos de Dados Parte 3 Evelin Carvalho Freire de Amorim 10 de abril de 2012 Nas aulas anteriores vimos os tipos numéricos (inteiro e real) e booleano. Nesta aula veremos três tipos de dados restantes do Haskell: caracteres, tuplas e listas. 1 Caracteres Caracteres não são tão interessantes matematicamante, porém na computação podemos utilizar os car- acteres para, por exemplo, processar palavras. Por qual motivo poderíamos querer processar palavras? Existem vários problemas que envolvem processar palavras: 1. Comparar duas palavras e verificar se as duas são iguais; 2. Contar o número de letrar em uma palavra; 3. etc. Em Haskell nós temos o tipo caracter para representar símbolos de uma palavra. Qualquer símbolo entre aspas simples é considerado um caracter. O tipo caracter é definido pela palavra Char. Veja a seguir alguns exemplos: Exemplo: 'a' '@' '1' Para processar caracteres de forma mais fácil foram criados vários padrões que associam um caracter a um número. Um dos primeiros padrões e o mais difundido foi o ASCII. São 128 caracteres associados a algum número. Veja na Figura 1 a tabela Ascii. Esta tabela já foi extendida para comportar símbolos de outras línguas. Figura 1: Tabela ASCII 1 A seguinte função em Haskell determina se um determinado caracter é um número. ehNum :: Char -> Bool ehNum x = '0' <= x && x <= '9' Veja que os operadores de comparação servem para carateres também. Podemos fazer funções pare- cidas para checar se um caracter é maísculo ou minúsculo. ehMaiscula :: Char -> Bool ehMaiscula x = 'A'<=x && x<='Z' ehMinuscula :: Char -> Bool ehMinuscula x = 'a'<=x && x<='a' Exercício 1): Faça uma função em Haskell que passe uma letra maíscula para minúscula e outra que faz o inverso. 1.1 Tuplas Uma forma de combinar tipos para formar novos é formando pares com eles. Por exemplo, (Integer,Char) consiste de todos os pares de valores para os quais o primeiro componente é um inteiro e o segundo um caracter. O tipo tupla aceita não apenas pares, mas quantos o usuário definir. Exemplo: ('a',1) (1,1,1) (1,(2.0,'a')) Segue aqui duas funções simples para uma tupla de dois elementos. primeiro :: (a,b) -> a primeiro (x,y) = x segundo :: (a,b) -> b segundo (x,y) = y Exercício 2) Teste as funções primeiro e segundo com os exemplos de tuplas acima. Exercício 3) Considere agora tuplas com 4 elementos. Faça as funções primeiro, segundo, terceiro e quarto, para pegar respectivamente o primeiro, o segundo, o terceiro e o quarto elemento da tupla. 1.2 Listas Uma lista é tipo de dado que representa um sequência de elementos, os quais devem ser dos mesmo tipos. No Haskell é possível a representação de listas infinitas, mas dado o escopo do nosso curso vamos apenas trabalhar com listas finitas. Uma lista finita é denotada utilizando chaves e vírgulas. Por exemplo, [1,2,3] é uma lista de três números e ['a','b'] é uma lista de dois caracteres. A lista vazia é representada por [] e uma lista com apenas um elemento a é escrita [a]. Exemplo: A seguir alguns exemplos de lista e seus tipos. [1,2,3] :: [Integer] ['h','a','s','k','e','l','l'] :: [Char] [ [1,2],[3]] :: [[Char]] Diferentemente de um conjunto uma lista pode ter elementos repetidos. Por exemplo, [1,1] é uma lista de dois elementos. O primeiro elemento de uma lista é chamado cabeça da lista e o a lista sem o primeiro elemento é chamado de cauda da lista. Por exemplo, a acabeça da lista [1,1] é 1 e a cauda dela é [1]. Em listas acrescenta-se um novo elemento usando-se um operador definido em Haskell como dois pontos �:� . Tal operador é o separador de um elemento da lista. Por exemplo: 2 Prelude> 1:[] [1] Prelude> 2:[1] [2,1] Veja que o elemento sempre é adicionado no início da lista. Agora digamos que queremos a cauda da lista, então definimos a função a seguir. cauda :: [a] -> [a] cauda (x:xs) = xs Primeiro vamos olhar a assinatura de tipo da função acima. Ele deve receber como entrada uma lista que possui elementos do tipo a e a saída é uma lista com elementos do tipo a. Então se chamamos a função acima devemos colocar uma lista, como a seguir 1 cauda [1,2,3,4,5] 2 ⇒ cauda 1:[2,3,4,5] 3 ⇒ [2,3,4,5] Na primeira linha fazemos a instânciação da função cauda. A segunda é possui um racícionio um pouco mais complicado. Sabemos que o operador �:� acrescenta um elemento na lista, então é razoável que [1,2,3,4,5] == 1:[2,3,4,5]. O resultado da aplicação de �1:� em [2,3,4,5] é o lado esquerdo da igualdade, então podemos afirmar que elas são equivalentes. Se são equivalente eu posso substituir um pelo outro, e desta forma chegamos no mesmo formato da definição da função cauda. Agora se eu quiser a cabeça da lista? Então a seguinte definição de função satisfaz como solução. cabeca :: [a] -> a cabeca (x:xs) = x Nesta solução temos o mesmo caso de cauda. Segue a instânciação e a resolução mecânica de um exemplo instânciando a função cabeca. 1 cabeca [1,2,3,4,5] 2 ⇒ cabeca 1:[2,3,4,5] 3 ⇒ 1 Existem também algumas operações que podem ser aplicadas em elementos do tipos lista. Posição: Para capturar um elemento em uma determinada posição na lista basta utilizar o operador !!. Exemplo: Prelude> [1,2,3,4,5]!!0 1 Prelude> [1,2,3,4,5]!!2 3 Concatenação: Duas listas podem ser concatenadas para formar uma lista maior. Esta função é denotada pelo operador binário ++. Prelude> [1,2,3]++ [4,5] [1,2,3,4,5] Prelude> [1,2] ++ [] ++ [1] [1,2,1] Assim como a soma na aritmética o operador ++ é associativo e tem como elemento identidade []. Em outras palavras: 3 (xs ++ ys) ++ zs = xs ++ (ys ++zs) [] ++ xs = xs ++ [] = xs para qualquer lista xs, ys, e zs. Tamanho: O tamanho de uma lista finita é o número de elementos que ela contém. A operação utilizada para saber o tamanho de uma lista é a função length. Prelude> length [1,2,3,4,5] 5 Prelude> length [] 0 Prelude> length ['a','b','c','d','e'] 5 Cabeça e Cauda: A função head do Haskell existe por padrão e sua saída é a cabeça. Segue alguns exemplos: Prelude> head [1,2,3,4,5] 1 Prelude> head [] *** Exception: Prelude.head: empty list Prelude> head ['a','b','c','d','e'] 'a' Exercício 4) Qual das igualdades abaixo são verdadeiras e quais são falsas? a) [[]] + +xs == xs b) [[]] + +xs == [xs] c) [[]] + +xs == [[], xs] d) [[]] + +[xs] == [[], xs] e) [xs] + +[] == [xs] f) [xs] + +[xs] == [xs, xs] Exercício 5) Qual o valor de [head xs] ++ tail xs quando xs é igual a []? 1.3 Strings As strings em Haskell são uma sequência de caracteres. Dado este fato, a representação interna das strings são listas de caracteres e todas as funções e operadores de listas servem para strings. A representação explícita de strings, no entanto, pode ser feitas através de aspas duplas. Veja um exemplo de uma função que pega o primeiro caracter de uma string. cabecaStr :: String -> Char cabecaStr (x:xs) = x Testando: Prelude> cabecaStr �teste� 't' Podemos redefinir a cabecaStr como se segue: cabecaStr :: [Char] -> Char cabecaStr (x:xs) = x E testá-la da mesma forma. Prelude> cabecaStr �teste� 't' 4