CAPÍTULO 05 Listas. Listas são estruturas de dados fundamentais da área de programação. Listas...

Preview:

Citation preview

CAPÍTULO 05

Listas

Listas

• Listas são estruturas de dados fundamentais da área de programação.

• Listas em Haskell representam coleções de objetos de um determinado tipo (homogêneos).

• Para um dado tipo t, existe o tipo "lista de elementos do tipo t", designado por [t].

• Por exemplo:[1,2,3,3,0,1] :: [Int][False,False, True] :: [Bool]

são uma lista de Int e uma lista de Bool.

Definição

• Uma lista sempre é composta de dois segmentos, exceto quando está vazia. Tais segmentos são a cabeça (head) e o corpo (tail).

Definição• Uma lista com cabeça a e cauda x

é escrita no formato (a:x) .• Atenção: em (a:x)

– a é um elemento da lista -> cabeça da lista

– x é uma outra lista -> cauda da lista (que é uma outra lista ou sub-lista)

• Observação: A lista vazia “[]” está presente em qualquer lista e o último elemento aponta para esta lista “[]”.

Definição

• Uma lista é formada por tipos de dados homogêneos, mas pode-se utilizar como alternativa uma lista de tuplas, na qual os dados das tuplas podem ser heterogêneos.

• Restrição: todos os elementos de uma lista devem ser do mesmo tipo!

Casamento de padrões

• (‘a’ : [’b’, ’c’, ’d’]) == [‘a’, ’b’, ’c’, ’d’]• (‘a’ : ’b’ : ’c’ : [’d’]) == [‘a’, ’b’, ’c’, ’d’]• (‘a’ : ’b’ : ’c’ : ’d’ : []) == [‘a’, ’b’, ’c’, ’d’]• [‘a’ : [‘b’, ‘c’, ‘d’] ] == [ [‘a’, ’b’, ’c’, ’d’] ]• (‘a’ : y) == [‘a’, ’b’, ’c’, ’d’]• (‘a’ : _) == [‘a’, ’b’, ’c’, ’d’]• (‘a’ : ‘b’ : [‘c’, ‘d’]) == [x : y : z]

Casamento de padrões

• Alguns erros típicos de casamento de padrões em listas:

• [‘a’ : ‘b’ : [‘c’, ‘d’] ] /= [‘a’, ’b’, ’c’, ’d’]

• [ [‘a’, ’b’, ’c’, ’d’] ] /= [‘a’, ’b’, ’c’, ’d’]

• [‘a’, ’b’, [’c’], ’d’] /= [‘a’, ’b’, ’c’, ’d’]

• [ [ [‘a’] : ’b’, ’c’, ’d’] ] /= [ [‘a’, ’b’, ’c’, ’d’] ]

• [‘a’, ’b’] /= [‘a’]

Operadores “:” e “++"

• Atenção: os operadores “:” e “++” são diferentes!

• O operador : recebe um elemento a e uma lista x, retornando uma nova lista onde a é o primeiro elemento.

• O operador ++ recebe duas listas e retorna sua concatenação! Uma lista única.

• Para memorizar:elemento : lista = listalista ++ lista = lista

Exemplos

• Comprimento (tamanho) da lista: Este é um exemplo clássico sobre listas.

• 1. O comprimento de uma lista vazia é zero;

• 2. O comprimento de uma lista (a:x) é dada por 1 mais o comprimento da sublista “x” que também é uma lista:

compto [] = 0 --função 1compto (a:x) = 1+compto x --função 2

Exemplos

• Um “mapa de memória aproximado” da execução de “compto [1, 2, 3, 4]” é dado por:

Exemplos

• Obter os N primeiros termos da lista:

• primeiros 0 _ = [ ]

• primeiros _ [ ] = [ ]

• primeiros n (a:x) = a : primeiros (n-1) x

• Execução:

Exemplos

• Adicionar um objeto a lista:

• Esta função adiciona um objeto na lista sem repetições, isto é, caso ele já exista na lista o mesmo não é adicionado:

insere c [ ] = [c]

insere c (a:x)|c == a = a : x

|otherwise = a : insere c x

A função “Observe”

• É uma das funções definidas na biblioteca Observe.hs. Destina-se a mostrar as diversas instâncias que variáveis e funções assumem durante a execução de um programa.

• Sintaxe:

import Observe

rem_ultimo [ _ ] = [ ]rem_ultimo (a:b) = observe “a” a : observe “rem_ultimo” rem_ultimo b

A função “Observe”

• O resultado de uma execução da função rem_ultimo com a função observe é dado por:

Main> rem_ultimo [2, 3, 4, 5, 6, 7, 7]

>>>>>>>> Observations <<<<<<<

a

2

3

4

5

6

7

A função “Observe”

rem_ultimo

{ \ (3 : 4 : 5 : 6 : 7 : _ : [ ]) -> 3 : 4 : 5 : 6 : 7 : [ ]

\ (4 : 5 : 6 : 7 : _ : [ ]) -> 4 : 5 : 6 : 7 : [ ]

\ (5 : 6 : 7 : _ : [ ]) -> 5 : 6 : 7 : [ ]

\ (6 : 7 : _ : [ ]) -> 6 : 7 : [ ]

\ (7 : _ : [ ]) -> 7 : [ ]

\ (_ : [ ]) -> [ ]

}

Compreensão de Lista

• A compreensão de listas é feita com um construtor de listas que utiliza conceitos e notações da teoria dos conjuntos. Por exemplo, seja o conjunto A definido por:

A = {x2 | X Є N ^ x é par}

• Este exemplo define o conjunto dos quadrados dos números pares.

• Em Haskell pode ser representado por uma lista definida da seguinte maneira:

Compreensão de Lista

constroi_lista = [x*x | x <- [9... 39], par x]par :: Int -> Boolpar x = mod x 2 == 0Execução:Main> par 26TrueMain> constroi_lista[100, 144, 196, 256, 324, 400, 484, 576,

676, 784, 900, 1024, 1156, 1296, 1444]

Strings

• As strings são formadas por uma seqüência de caracteres, um após o outro, formando uma lista em particular com o tipo caracter ou Char.

Prelude> “Oi Mundo”

“Oi Mundo”

Prelude> [‘O’, ‘i’, ‘ ’, ‘M’, ‘u’, ‘n’, ‘d’, ‘o’]

“Oi Mundo”

Prelude> (‘O’ : ‘i’ : ‘ ’ : ‘M’ : ‘u’ : [‘n’, ‘d’, ‘o’])

“Oi Mundo”

Conversões entre tipos

• A função show apresenta na tela strings e valores numéricos em geral.

• Em particular, a função putStr, imprime uma palavra do tipo String.

• A função read lê um conteúdo e o converte a numérico se for o caso.

• Exemplos:Prelude> “O quadrado de 7 eh: ” ++ show (7 * 7)

“O quadrado de 7 eh: 49”

Conversões entre tipos

Prelude> show (log 4567)“8.426611813185”

Prelude> read “77” + 380

Prelude> read “77 + 56” + 33Program error: Prelude.read: no parse

Prelude> show (read “77” + log 100)“81.6055170185”

Caracteres Especiais

• Alguns caracteres especiais dentro de uma string produzem efeitos na saída, quando sob a função putStr:

• \t ► Produz um espaço-padrão tabulado na saída.

• \n ► Força um salto de linha.• \\ ► Torna uma barra invertida um

caracter normal.• \’ ► Idem para aspas simples (’).• \” ► Idem para aspas duplas (”).

Avaliação Preguiçosa

• Um exemplo clássico de avaliação preguiçosa é exibida por:

lista_infinita :: Int -> [Int]

lista_infinita n = n : lista_infinita (n+1)

...

lista_infinita 3 = 3 : lista_infinita (3+1)

= 3 : 4 : lista_infinita (4+1)

= 3 : 4 : 5 : lista_infinita (5+1)

...

...

Avaliação Preguiçosa

• Esta função computa infinitos valores para uma lista de inteiros, a partir de um número passado como parâmetro. A soma não é determinada até que necessária. Como não há critério de parada, esta, cresce indefinitivamente.

Padrões em Haskell

• Como já visto, em Haskell há casamentos com constantes inteiras, booleanas, caracteres, strings;

• Variáveis;• Tuplas de padrões; ex: (p1,p2,...) onde p1,p2,... também são padrões.

• Listas de padrões; ex: (p1:p2) onde p1 e p2 também são padrões.

• O padrão “_” (o “underscore”);• Construtores de padrões: ( .. : ..), case

Padrões em Haskell

• Quando um argumento a casa com um padrão p?– se p é uma constante e p=a; Ex: 7 == 7– se p é uma variável x, sendo que a será associado a x;– se p é uma tupla (p1,p2,...), a=(a1,a2,...) e cada

componente de a casa com o respectivo componente de p.Ex: (3,1,2) == (3,1,2)

True – se p é uma lista (p1:p2), a é uma lista não vazia e a

cabeça de a casa com p1 e sua cauda casa com p2.– Ex: (1 , 2 , [4 .. 40] ) == (1 , 2, [4 .. 40])

True

Padrões em Haskell

Quanto ao “_” ?{- inicio: uma nova lista, sem o último elemento -} inicio :: [a] -> [a] inicio [ _ ] = [] inicio (x:xs) = observe "x" x : observe "inicio" inicio xs

inicio [1 .. 7][1,2,3,4,5,6]

Não esquecer:

module Listas whereimport Observe

Padrões em Haskellinicio [1 .. 7][1,2,3,4,5,6]

>>>>>>> Observations <<<<<<

x 1 2 3 4 5 6

inicio { \ (2 : 3 : 4 : 5 : 6 : 7 : []) -> 2 : 3 : 4 : 5 : 6 : [] , \ (3 : 4 : 5 : 6 : 7 : []) -> 3 : 4 : 5 : 6 : [] , \ (4 : 5 : 6 : 7 : []) -> 4 : 5 : 6 : [] , \ (5 : 6 : 7 : []) -> 5 : 6 : [] , \ (6 : 7 : []) -> 6 : [] , \ (7 : []) -> [] }

Padrões em Haskell

• Restrição importante: não é permitido usar um mesmo nome mais de uma vez dentro do mesmo padrão!

• Exemplo:

ERRO – variável repetida em padrão!!! (Repeated variable "x" in

pattern)

• Solução :

1 .2 .3 .

1 .2 .3 .

func :: Int -> Int -> Boolfunc x x = Truefunc x y = False

1 .2 .3 .

1 .2 .3 .

func :: Int -> Int -> Boolfunc x y | x == y = Truefunc x y = False

Aplicação: BD para Biblioteca

• Uma biblioteca usa um banco de dados para manter um registro dos livros emprestados aos clientes.

• Pessoas e livros serão representados por Strings.

• O BD será uma lista de pares (Pessoa,Livro).

• Se um par (p,b) estiver no BD, isso significa que o cliente p pegou um exemplar do livro b emprestado.

Tipos

• Exemplo de BD:

exBD = [ ("Alice", "Senhor dos Anéis"), ("Ana", "Windows 3.1"), ("Alice", "Excel para Leigos"), ("Ricardo", "Senhor dos Anéis") ]

1 .2 .3 .

1 .2 .3 .

type Pessoa = Stringtype Livro = Stringtype BancoDados = [ (Pessoa, Livro) ]

Funções de Pesquisa

• As funções de pesquisa em BD recebem como entrada um BD, uma pessoa ou livro, e retornam o resultado da pesquisa.

• Exemplos:– Dada uma pessoa, encontrar os livros emprestados por

ela.– Dado um livro (pode ter mais de uma cópia), listar as

pessoas que estão com o mesmo emprestado.– Dado um livro, descobrir se está emprestado.– Dada uma pessoa, calcular o número de livros que ela tem

emprestados no momento.livros :: BancoDados -> Pessoa -> [Livro]clientes :: BancoDados -> Livro -> [Pessoa]emprestado :: BancoDados -> Livro -> BoolnumEmprest :: Bancodados -> Pessoa -> Int

Funções de Atualização

• As funções de atualização recebem como entrada um BD e retornam um novo BD, resultado de uma operação.

• Exemplos:– Emprestar um livro a um cliente.– Devolver um livro emprestado.

fazEmprest :: BancoDados -> Pessoa -> Livro -> BancoDadosretEmprest :: BancoDados -> Pessoa -> Livro -> BancoDados

Definindo as funções de Pesquisa

• Definir a função: livros :: BancoDados -> Pessoa -> [Livro]

• As funções clientes, emprestado, numEmprest podem ser definidas de forma semelhante.

1 .2 .3 .4 .5 .

1 .2 .3 .4 .5 .

livros :: BancoDados -> Pessoa -> [Livro]livros [] p = []livros ((n,b) : resto) p | p == n = b : livros resto p | otherwise = livros resto p

Definindo as funções de Atualização

• Definir a função: fazEmprest :: BancoDados -> Pessoa ->

Livro -> BancoDados

• Basta adicionar um novo par ao banco de dados (pode haver repetições).

• Pesquisar o BD e remover a primeira ocorrência do par (pes,liv).

• Se não existir esse par no BD, é caracterizada uma situação de erro!

1 .2 .3 .

1 .2 .3 .

fazEmprest:: BancoDados -> Pessoa -> Livro -> BancoDadosfazEmprest db p b = (p,b) : db

Definindo as funções de Atualização

retEmprest:: BancoDados -> Pessoa -> Livro -> BancoDados

retEmprest ((p,b) : resto) pes liv | p == pes && b == liv = resto | otherwise = (p,b) : retEmprest resto pes liv

retEmprest [] pes liv = error ("falha em retEmprest:" ++ pes ++ " " ++ liv)

Cancela apenas a primeira ocorrência do par.Cancela apenas a primeira ocorrência do par.

Definindo as funções de Atualização

• Definição alternativa, cancelando todas as ocorrências do par:

retEmprest:: BancoDados -> Pessoa -> Livro -> BancoDados

retEmprest ((p,b) : resto) pes liv | p==pes && b==liv = retEmprest resto pes liv | otherwise = (p,b) : retEmprest resto pes liv

retEmprest [] pes liv = []

Conclusão

• Listas e suas funções são TAD’s do Haskell

• O poliformismo de tipos é evidenciado. Aplica-se uma função igualmente a todos objetos da lista. Uma função é reusada, sobre vários tipos!

• Uma função serve há vários tipos de listas.• Há muitas funções prontas (“built-in”), já

definidas pelo ambiente.

Recommended