Estruturas de dados puramente funcionais - Dia...

174
Estruturas de dados puramente funcionais Dia 1 Emilio Francesquini [email protected] 2019.Q3 Centro de Matemática, Computação e Cognição Universidade Federal do ABC

Transcript of Estruturas de dados puramente funcionais - Dia...

Page 1: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Estruturas de dados puramente funcionaisDia 1

Emilio [email protected]

Centro de Matemática, Computação e CogniçãoUniversidade Federal do ABC

Page 2: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Outline

1 Introdução

2 Estruturas funcionais vs. estruturas imperativas

3 Estruturas de dados persistentes

4 Árvores

5 Roseiras

6 Árvores Rubro-Negras

7 Type-safe Red-Black Trees

8 Referências

1

Page 3: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Introdução

Page 4: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

1 {-# LANGUAGE DataKinds #-}2 {-# LANGUAGE KindSignatures #-}3 {-# LANGUAGE GADTs #-}4 {-# LANGUAGE StandaloneDeriving #-}5

6 {-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}7

8 module Dia01 where9

10 import Prelude hiding ((++))11 import Data.Maybe

2

Page 5: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Introdução

Quando programadores de C, C++, Java, Python, … precisam deuma estrutura de dados específica:

� Usam uma biblioteca.I Legal! Também funciona em funcional! (sic)

� Abrem um livro como o [CLRS] e copiam o código.I Infelizmente, programadores de linguagens funcionaiscomo Haskell, e ML não podem se dar a esse luxo.

2

Page 6: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Introdução

Apesar desses livros se dizerem ”independentes da linguagemde programação”, eles são independentes apenas no sentidodo Henry Ford:

Programadores podem usar a linguagem que quise-rem, contanto que ela seja imperativa1.

Queremos ajudar a resolvereste problema!

1Henry Ford certa vez disse sobre as cores disponíveis para o Ford T: ”[Osclientes] podem escolher a cor que quiserem, contanto que seja preta” 3

Page 7: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Objetivos

� Mostraremos como implementar e manipular algumasestruturas de dados clássicas no contexto funcional.

� Além disto também apresentaremos algumas estruturasnascidas no contexto funcional para lidar com as suaspeculiaridades como imutabilidade, persistência,transparência referencial, avaliação preguiçosa, …

4

Page 8: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Estruturas funcionais vs. estruturasimperativas

Page 9: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Linguagens funcionais

� Apesar de muitos reconhecerem os benefícios daslinguagens funcionais, até recentemente elas eram poucoutilizadas e até mesmo pouco conhecidas.

� Isso explica, em parte, porque os códigos existentesatualmente são majoritariamente imperativos.

� Além disto, historicamente, linguagens funcionais tinhamum desempenho bem inferior ao de linguagenstradicionais. Isso tem mudado bastante com a evoluçãode compiladores que fazem análises sofisticadas decódigo.

5

Page 10: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Compiladores e estruturas de dados

� De qualquer modo, mesmo com compiladoresespetaculares é improvável que eles sejam capazes deresolver problemas de uso de estruturas de dados comdesempenhos ruins.

Então basta usarmos boas estruturas!

6

Page 11: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Mais fácil falar do que fazer…

Mas porque desenvolver estruturas de dados funcionais émais difícil que estruturas imperativas?

7

Page 12: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Estruturas de dados funcionais

� Motivo 1 - Inexistência de atualizações destrutivas

Atualizações destrutivas exigem cuidado em seu uso, porémpodem ser extremamente poderosas e eficientes se bemutilizadas.

8

Page 13: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Estruturas de dados funcionais

� Motivo 2 - Aumento de expectativa

Em uma estrutura imperativa, raramente esperamos que elaseja persistente (en: persistent⬌ ephemeral).

📝 Note� Estruturas de dados persistentes são aquelas quepermitem a existência simultânea de diversas versões daestrutura (tipicamente com compartilhamento de dados).

� Estruturas de dados efêmeras são aquelas que permitema existência de apenas uma versão da estrutura de dadossimultaneamente.

9

Page 14: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Paradigma funcional vs. imperativo

� Em linguagens de programação funcional, estruturas dedados funcionais são sempre persistentes.

� Em linguagens imperativas, estruturas de dados sãotipicamente efêmeras e, quando programadores destaslinguagens precisam de estruturas persistentes, eles nãose surpreendem e aceitam estruturas cuja facilidade deuso, implementação e complexidade computacional sãosuperiores às suas versões efêmeras.

10

Page 15: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Expectativas aumentadas

Queremos, então:

� Persistência.� Mesma complexidade computacional de EDs efêmeras.

Não nos ajuda o fato de que é possível mostrar que, em algunscasos, linguagens funcionais são intrinsecamente menoseficientes que suas colegas imperativas.

� Era de se esperar, já que o hardware é imperativo.

11

Page 16: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Jogamos a toalha?

12

Page 17: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Ainda há esperança!

� A verdade é que, na prática, a coisa não é tão assustadoraassim!I Para praticamente todas as estruturas de dados cobertasem um curso de graduação de computação, já seconhecem implementações funcionais cuja complexidadeassintótica é a mesma das versões imperativas.

I Neste curso vamos explorar algumas estruturas de dadosfuncionais mais comuns. Mas não vamos dar um catálogo,vamos, na verdade, mostrar as técnicas usadas para quevocê possa desenvolver a sua própria ED quando precisar.

13

Page 18: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Estruturas de dados persistentes

Page 19: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Checklist

Uma propriedade que se torna evidente em estruturas dedados (EDs) funcionais é que elas são sempre persistentes

� Atualizações em uma ED não destroem a versão antiga.� Elementos afetados são copiados.� Elementos não afetados são compartilhados (en: shared)entre as diversas versões da ED.

14

Page 20: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Um primeiro exemplo: Listas

Listas são onipresentes em linguages funcionais e sãoexpressas naturalmente em cálculo-λ. As operações que elasdão suporte são normalmente as mesmas daquelas oferecidaspor pilhas (en: stacks)

� nova :: Pilha a Cria uma nova pilha vazia� vazia :: Pilha a -> Bool Verdadeiro se pilha vazia� empilha :: a -> Pilha a -> Pilha a Empilha umvalor

� topo :: Pilha a -> a Recupera o valor no topo dapilha

� desempilha :: Pilha a -> Pilha a Desempilhaum valor

15

Page 21: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementação 1 - Na Unha1 data Pilha a = Vazia | Cons a (Pilha a)2

3 nova :: Pilha a4 nova = Vazia5

6 vazia :: Pilha a -> Bool7 vazia Vazia = True8 vazia _ = False9

10 empilha :: a -> Pilha a -> Pilha a11 empilha = Cons12

13 topo :: Pilha a -> a14 topo Vazia = error "Pilha vazia"15 topo (Cons v _) = v16

17 desempilha :: Pilha a -> Pilha a18 desempilha Vazia = error "Pilha vazia"19 desempilha (Cons _ p) = p 16

Page 22: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementação 2 - Listas padrão do Haskell

Utilizando as listas presentes por padrão em Haskell teríamos:1 type Pilha' a = [a]2

3 nova' :: Pilha' a4 nova' = []5

6 vazia' :: Pilha' a -> Bool7 vazia' = null8

9 empilha' :: a -> Pilha' a -> Pilha' a10 empilha' = (:)11

12 topo' :: Pilha' a -> a13 topo' = head14

15 desempilha' :: Pilha' a -> Pilha' a16 desempilha' = tail

17

Page 23: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Terminologia

A partir de agora, seja uma lista ou uma pilha, utilizaremos anomenclatura tradicional de listas.

� Pilhas, de fato, são uma instância do caso mais geral desequências. Outras instâncias que veremos durante ocurso incluem: filas, filas de duas extremidades e listascatenárias.

💡 TipMomento cultural

Concatenar tem a mesma raiz etimológica de catenária eambas vêm do latim catena: cadeia, corrente… Em inglês háinclusive o verbo catenate equivalente ao nosso concatenar.

18

Page 24: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Catenárias…

� Falando em catenária, outra operação comum em listas éa concatenação, normalmente representada pelooperador (++).

� A concatenação de listas pode ser implementadafacilmente em linguagens imperativas em tempoconstante (O(1)).

19

Page 25: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Concatenação efêmera

� Porém, essa implementação imperativa é destrutiva emseus parâmetros. Após a execução de (++), nem xs nemys podem ser usadas novamente.I Não possui persistência.

� Em uma linguagem funcional não há a possibilidade defazer operações destrutivas, então a saída é fazer umaimplementação que copia células, mantendo os valores etrocando o valor do apontador para a próxima célula.

20

Page 26: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Concatenação persistente

� A implementação da operação (++) fica:

1 (++) :: [a] -> [a] -> [a]2 (++) [] ys = ys3 (++) (x:xs) ys = x : (xs ++ ys)

A figura abaixo representa essa operação

21

Page 27: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Concatenação persistente

� Ganhamos persistência, mas agora o custo passou a serO(n) onde n, é o comprimento de xs.I Veremos mais adiante como reduzir esse custo para O(1).

� Um caso parecido ocorre quando queremos fazer aatualização de um elemento com índice i da lista. Nestecaso, precisamos copiar as células da lista ligada até oelemento desejado.

22

Page 28: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Atualizações persistentes

O código fica:

1 update :: [a] -> Int -> a -> [a]2 update [] _ _ = error "Tentando atualizar lista vazia"3 update (_:xs) 0 v = v:xs4 update (x:xs) i v = x : update xs (i - 1) v

Graficamente ys = update xs 2 7:

23

Page 29: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Atualizações persistentes

Apenas para reforçar: copiamos apenas o caminho até oelemento desejado.

� WarningEssa implementação se baseia fortemente no coletor de lixo(en: garbage collector). Implementar algo dessa maneiracom controle de memória manual ficaria rapidamentecomplicadíssimo por conta dos compartilhamentos. 24

Page 30: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Exercício 1

1 Escreva uma função sufixos :: [a] -> [[a]] querecebe uma lista xs e devolve uma lista com todos ossufixos da lista xs em ordem decrescente decomprimento. Por exemplo:

1 sufixos [1, 2, 3, 4] =2 [[1,2,3,4],[2,3,4],[3,4],[4],[]]

2 Mostre que o resultado pode ser gerado em tempo O(n) erepresentado em espaço O(n).

25

Page 31: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Percorrendo listas

� Percorrer listas do início em direção ao fim é fácil eeficiente.

� Percorrer listas no sentido contrário é um pouco maiscomplicado.

� Uma lista em um contexto funcional é muito parecida comuma lista ligada com encadeamento simples utilizadacorriqueiramente em linguagens imperativas.

26

Page 32: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Listas duplamente encadeadas - Tentativa de implementação

Como fazer uma lista duplamente encadeada caso queiramospercorrê-la em ambas as direções?

1 -- esquerda valor direita2 data Lista2 a = Vazia2 | Cons2 (Lista2 a) a (Lista2 a)3

4 null2 :: Lista2 a -> Bool5 null2 Vazia2 = True6 null2 _ = False7

8 head2 :: Lista2 a -> a9 head2 Vazia2 = error "Lista vazia"

10 head2 (Cons2 _ x _) = x11

12 tail2 :: Lista2 a -> Lista2 a13 tail2 Vazia2 = Vazia214 tail2 (Cons2 _ _ d) = d 27

Page 33: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Listas duplamente encadeadas - Tentativa de implementação

Parece bom! Mas e agora, como implementar a função cons2?

1 cons2 :: a -> Lista2 a -> Lista2 a2 cons2 = undefined -- ??

28

Page 34: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Listas duplamente encadeadas - Tentativa de implementação

� O problema é que, agora, o nó seguinte da nossa listaaponta para o anterior. Modificar o nó inicial da listacausa uma alteração em cascata até o final da lista!

29

Page 35: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Listas duplamente encadeadas - Tentativa de implementação

� Toda a lista precisa ser copiada! Não hácompartilhamento!

� Colocar algo no fim da lista ou concatenar têm o mesmoproblema.

� Temos o que queríamos, mas a que custo?

30

Page 36: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Zippers

� Zipper é um idiom (do inglês: expressão idiomática) delinguagens funcionais muito utilizado para manipularestruturas de dados persistentes.

� Zippers podem ser usados não apenas para percorrerestruturas de dados como também para auxiliar em suamodificação.

📝 NoteAqui veremos o uso aplicado de Zippers, mas existemmaneiras formais de se definir um zipper para uma estruturade dados qualquer. Na verdade um zipper é definido como aderivada da ADT2.

2http://pesquisa.ufabc.edu.br/haskell/posts/categorias/06-ADT.html 31

Page 37: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Zippers

� Mas afinal, o que é um zipper?

Zippers funcionam de uma maneira parecida com aquela pelaqual uma formiga explora o mundo.

� A localização atual da formiga é o foco do zipper.� A formiga pode se deslocar para explorar o mundoseguindo os caminhos disponíveis.

� Para poder se orientar, ela deixa por cada local por ondepassou uma trilha de feromônio que pode ser utilizadapara retornar pelo mesmo caminho por onde ela veio.

32

Page 38: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Zipper

� Esta última característica permite que implementemos,por exemplo, backtracking em linguagens funcionais demaneira eficiente.

� No caso específico de uma lista, a formiga/zipper, quemora em uma lista, explora um mundo unidimensional:ela só pode avançar ou retroceder.

� Como implementar tal abstração?

33

Page 39: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

List Zippers

� Simples! 2 listas!I Uma contém o que ainda não foi percorrido da lista. Tudoà direita do foco.

I A outra contém uma trilha de migalhas, a trilha deferomônio da nossa formiga, para que ela possa voltar poronde veio. Tudo à esquerda do foco.

1 type ListZipper a = ([a], [a])

34

Page 40: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

List Zippers

1 toListZipper :: [a] -> ListZipper a2 toListZipper xs = ([], xs)3

4 fromListZipper :: ListZipper a -> [a]5 fromListZipper (es, ds) = reverse es ++ ds6

7 lzFoco :: ListZipper a -> Maybe a8 lzFoco (_, []) = Nothing9 lzFoco (_, x:_) = Just x

10

11 lzDir :: ListZipper a -> Maybe (ListZipper a)12 lzDir (_, []) = Nothing13 lzDir (es, d:ds) = Just (d:es, ds)14

15 lzEsq :: ListZipper a -> Maybe (ListZipper a)16 lzEsq ([], _) = Nothing17 lzEsq (e:es, ds) = Just (es, e:ds) 35

Page 41: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

List Zippers

� Tudo muito bem e muito bonito, mas além de percorrer alista como uso isso para modificá-la?

� Imagine que temos uma lista de inteiros e queremostrocar todos os números 5 por 42.I Vamos definir, primeiramente, uma função para trocar ovalor do elemento em foco.

1 lzTrocaFoco :: a -> ListZipper a -> Maybe (ListZipper a)2 lzTrocaFoco _ (_, []) = Nothing3 lzTrocaFoco y (es, _:ds) = Just (es, y:ds)

36

Page 42: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

List Zippers

Agora podemos definir a função que troca os cincos:

1 trocaCincos :: [Int] -> [Int]2 trocaCincos [] = []3 trocaCincos xs =4 fromListZipper $ trocaZip $ toListZipper xs5 where6 trocaZip z@(_, []) = z7 trocaZip z@(_, d:_) =8 let nz = if d == 59 then fromJust $ lzTrocaFoco 42 z

10 else z in11 maybe nz trocaZip (lzDir nz)

1 -- Mas pra que tudo isso?2 trocaCincos' :: [Int] -> [Int]3 trocaCincos' xs = [if x == 5 then 42 else x | x <- xs] 37

Page 43: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Mas pra quê tudo isso?

� Listas são estruturas de dados relativamente simples. Porisso a manipulação de um elemento é fácil…I … e existem mecanismos como compreensão de listas quenos ajudam a fazer o que queremos.

38

Page 44: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Mas pra quê tudo isso 2?

� Pense agora em estruturas um pouco mais elaboradas,como árvores ou grafos.I O fato de estarmos lidando com uma linguagem funcional(e EDs persistentes) impede que simplesmente alteremosum valor de um nó na árvore através de um ponteiro!

I Precisamos substituir o nó que queremos modificar porum novo e ”consertar” todo o caminho por onde passamospara que ele inclua as novas versões dos dados.

O caminho por onde passamos é justamente o caminhoarmazenado no Zipper!!

39

Page 45: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Exercício 2

Estude a função trocaCincos e as funções que ela utiliza.Em seguida reimplemente a função fromListZipper ::ListZipper a -> [a]. Você não pode utilizar as funções(++) ou reverse (nem reimplementá-las!).

1 trocaCincos :: [Int] -> [Int]2 trocaCincos [] = []3 trocaCincos xs =4 fromListZipper $ trocaZip $ toListZipper xs5 where6 trocaZip z@(_, []) = z7 trocaZip z@(_, d:_) =8 let nz = if d == 59 then fromJust $ lzTrocaFoco 42 z

10 else z in11 maybe nz trocaZip (lzDir nz)

40

Page 46: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores

Page 47: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores binárias

� Padrões de compartilhamento mais interessantes podemaparecer quando temos mais de um campo por nó.

� Árvores binárias apresentam esses padrões.

41

Page 48: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores binárias de busca

Árvores binárias de busca são árvores binárias cujoselementos internos são organizados em ordem simétrica.

� Ou seja, o valor contido por um nó qualquer é maior quetodos os valores armazenados em sua sub-árvoreesquerda e menor que todos os elementos armazenadosem sua sub-árvore direita.

� Nesta definição assumimos que a árvore só conteráelementos que possuem uma ordem total. Ou, em outraspalavras, não admitimos elementos repetidos.

42

Page 49: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores binárias de busca

1 data Arv a = ArvVazia | No (Arv a) a (Arv a)

� Vamos usar essa representação para implementarConjuntos.

� Contudo, é fácil de adaptá-la para outras abstrações comoMapas ou para outras operações (como encontrar oi-ésimo menor elemento) acrescentando alguns valoresna definição da nossa árvore.

43

Page 50: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Uma classe de tipo para conjuntos

O código abaixo define uma typeclass para um conjunto:

1 class Conjunto t where2 membro :: Ord a => a -> t a -> Bool3 insere :: Ord a => a -> t a -> t a

📝 NoteUma typeclass (classe de tipos) é semelhante a uma interfaceque define algum comportamento. A nossa typeclassConjunto define dois métodos: membro e insere.Qualquer tipo x que deseje ”participar”desta typeclassprecisa fornecer implementações de funções para estes doismétodos. Quando isto é feito dizemos que declaramos umainstância da typeclass para o tipo x.

44

Page 51: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Uma classe de tipo para conjuntos

� WarningNão caia na confusão de achar que os termos comuns emorientação a objeto classes, instâncias e métodos têmqualquer coisa a ver com os termos homônimos em Haskell!Eles não tem relação!

45

Page 52: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores como conjuntos

1 data Arv a = ArvVazia | No (Arv a) a (Arv a)

E para tornar nossa árvore um conjunto podemos fazer:

1 instance Conjunto Arv where2

3 membro _ ArvVazia = False4 membro x (No e v d)5 | x < v = membro x e6 | x > v = membro x d7 | otherwise = True8

9 insere x ArvVazia = No ArvVazia x ArvVazia10 insere x t@(No e v d)11 | x < v = No (insere x e) v d12 | x > v = No e v (insere x d)13 | otherwise = t 46

Page 53: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores

A execução da função membro é trivial. Vamos verificar commais cuidado a execução de insere com o elemento e:

Antes

d

b

a c

g

f h

exs

47

Page 54: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores

Depois

d

b

a c

g

f h

d

g

f

e

xs ys

48

Page 55: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores

� Cada nó que foi copiado, ou seja aqueles que foramafetados pela inserção, tem ao menos uma sub-árvorecompartilhada com a árvore inicial. Na verdade, sãocopiados apenas os nós no caminho da inserção que, parauma árvore que for balanceada, é proporcional a O(logn).

� Em um sistema real a maior parte dos nós reside nassub-árvores compartilhadas.

49

Page 56: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Percorrendo árvores

� Os três percursos tradicionais de árvores,pré/in/pós-ordem podem ser aplicados de maneira trivial.

d

b

a c

g

f h

1

2

3 4

5

6 71

2

3

4

5

6

71 2

3

4 5

6

71

2 3

4 5 6 7Pré-ordem: d, b, a, c, g, f, hIn-ordem: a, b, c, d, f, g, hPós-ordem: a, c, b, f, h, g, dLargura: d, b, g, a, c, f, h 50

Page 57: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Percorrendo árvores

Ou em código que imprime o conteúdo dos nós:

1 preOrdem, inOrdem, posOrdem :: Show a => Arv a -> IO ()2 preOrdem ArvVazia = return ()3 preOrdem (No e x d) = do4 print x5 preOrdem e6 preOrdem d7 inOrdem ArvVazia = return ()8 inOrdem (No e x d) = do9 inOrdem e

10 print x11 inOrdem d12 posOrdem ArvVazia = return ()13 posOrdem (No e x d) = do14 posOrdem e15 posOrdem d16 print x 51

Page 58: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Percorrendo em largura

� Mas e se quisermos uma busca em largura? Asimplementações tradicionais para percorrer uma árvorelançam mão de uma fila.

� Vamos usar a implementação de fila fuleira® baseada emlistas.

1 -- Fila "fuleira"®2 type Queue a = [a]3

4 -- O(1)5 empty :: Queue a6 empty = []7

8 -- O(1)9 isEmpty :: Queue a -> Bool

10 isEmpty = null52

Page 59: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Operações na fila fuleira®

1 -- O(1)2 enq :: Queue a -> a -> Queue a3 enq = flip (:)4

5 -- O(n)6 deq :: Queue a -> (a, Queue a)7 deq xs = (last xs, init xs)

53

Page 60: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

BFS - Versão com fila fuleira®

1 larguraQ :: Show a => Arv a -> IO ()2 larguraQ arv =3 larguraFila $ enq empty arv4 where5 larguraFila q =6 if isEmpty q then return ()7 else case deq q of8 (ArvVazia, q') -> larguraFila q'9 (No e x d, q') -> do

10 print x11 larguraFila $ enq (enq q' e) d

54

Page 61: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

BFS - Versão com fila fuleira ®

� Com as estruturas de dados que temos até agora (apenaslistas), apesar de possível, ainda não é trivial implementaruma estrutura de dados para fila que seja eficiente.

💡 TipJá é possível, contudo, implementar uma ED comcomplexidade amortizada O(1) (falaremos mais sobre issomais adiante no curso).

55

Page 62: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Quem não tem cão, caça com gato!

� Uma outra abordagem (sem usar filas) é utilizar umalgoritmo baseado em níveis:

1 larguraNivel :: Show a => Arv a -> IO ()2 larguraNivel arv =3 larguraNivel' [arv]4 where5 printArv ArvVazia = return ()6 printArv (No _ x _) = print x7

8 filhos ArvVazia = []9 filhos (No e _ d) = [e, d]

10

11 larguraNivel' [] = return ()12 larguraNivel' lvl = do13 mapM_ printArv lvl14 larguraNivel' $ concatMap filhos lvl 56

Page 63: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Numerando os nós em largura

� Vamos considerar agora um problema um poucodiferente: numeração dos nós em largura (en:breadth-first numbering, BFN).

� Considere que queremos uma função:

1 bfn :: Arv a -> Arv (a, Int)

onde o segundo elemento da tupla é a ordem em queeste elemento foi visitado pela varredura.

� Se fosse para fazer os casos tradicionais (a.k.a. fáceis)pré/in/pós-ordem…

57

Page 64: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Numerando os nós

1 numeraPreOrdem :: Arv a -> Arv (a, Int)2 numeraPreOrdem arv =3 snd $ numeraPreOrdem' 1 arv4 where5 numeraPreOrdem' i ArvVazia = (i, ArvVazia)6 numeraPreOrdem' i (No e x d) =7 (i3, No e' (x, i) d')8 where9 (i2, e') = numeraPreOrdem' (i + 1) e

10 (i3, d') = numeraPreOrdem' i2 d

Versão consulta:1 preOrdem ArvVazia = return ()2 preOrdem (No e x d) = do3 print x4 preOrdem e5 preOrdem d 58

Page 65: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Numerando os nós

1 numeraInOrdem :: Arv a -> Arv (a, Int)2 numeraInOrdem arv =3 snd $ numeraInOrdem' 1 arv4 where5 numeraInOrdem' i ArvVazia = (i, ArvVazia)6 numeraInOrdem' i (No e x d) =7 (i3, No e' (x, i2) d')8 where9 (i2, e') = numeraInOrdem' i e

10 (i3, d') = numeraInOrdem' (i2 + 1) d

Versão consulta:1 inOrdem ArvVazia = return ()2 inOrdem (No e x d) = do3 inOrdem e4 print x5 inOrdem d 59

Page 66: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Numerando os nós

1 numeraPosOrdem :: Arv a -> Arv (a, Int)2 numeraPosOrdem arv =3 snd $ numeraPosOrdem' 1 arv4 where5 numeraPosOrdem' i ArvVazia = (i, ArvVazia)6 numeraPosOrdem' i (No e x d) =7 (i3 + 1, No e' (x, i3) d')8 where9 (i2, e') = numeraPosOrdem' i e

10 (i3, d') = numeraPosOrdem' i2 d

Versão consulta:1 posOrdem ArvVazia = return ()2 posOrdem (No e x d) = do3 posOrdem e4 posOrdem d5 print x 60

Page 67: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

BFN - Versão com fila

� Já a versão em largura é um pouco mais trabalhosa…

1 bfn1 t =2 fst $ deq $ bfn' 1 (enq empty t)3 where4 bfn' i inQ5 | isEmpty inQ = empty6 | otherwise =7 case deq inQ of8 (ArvVazia, inQ1) -> enq (bfn' i inQ1) ArvVazia9 (No e x d, inQ1) ->

10 let11 inQ2 = enq (enq inQ1 e) d12 outQ0 = bfn' (i + 1) inQ213 (d', outQ1) = deq outQ014 (e', outQ2) = deq outQ1 in15 enq outQ2 (No e' (x, i) d')

61

Page 68: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

BFN - Versão com fila

� Apesar de, a princípio, esta versão parecer complicada elaé bem eficiente.

� Se a fila utilizada tiver complexidade O(1) para inserção eremoção, então o algoritmo roda em tempo O(n).

62

Page 69: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

BFN - Versão pavorosa (a.k.a. por níveis)

1 bfn2 arv = head $ bfn' 1 [arv]2 where3 filhos ArvVazia = []4 filhos (No a _ b) = [a, b]5

6 recons _ [] [] = []7 recons i (ArvVazia:ts) cs= ArvVazia : recons i ts cs8 recons i ~(No _ x _ : ts) ~(a : b : cs) =9 No a (x, i) b : recons (i + 1) ts cs

10

11 bfn' _ [] = []12 bfn' i lvl =13 let14 proxNivel = concatMap filhos lvl15 j = i + length proxNivel `div` 216 proxNivelNum = bfn' j proxNivel in17 recons i lvl proxNivelNum

63

Page 70: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

BFN - Versão pavorosa (a.k.a. por níveis)

� Além de pavorosa, essa versão não é nem um poucoeficiente. Ela varre cada nível 3 vezes!I Uma vez para pegar os filhos.I Uma segunda vez para computar o comprimento.I Uma terceira para reconstruir o nível.

� Veremos mais adiante como fazer isto de maneira maiseficiente e simples explorando a avaliação preguiçosadisponível em praticamente todas as linguagens deprogramação funcional.

64

Page 71: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Um zipper para árvores

Figura 1: Fonte: Street Art Utopia 65

Page 72: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Um zipper para árvores

� Diferentemente do caso de listas, zippers para árvores nãosão unidimensionais.

� Para percorrer uma árvore podemos:I ir em direção às folhas (no caso de uma árvore bináriaseguindo pelos filhos à esquerda ou à direita).

I ir em direção à raiz (para cima).

Assim, nosso zipper terá as seguintes opções de deslocamento:

� esq → caminha em direção às folhas pelo filho esquerdo� dir → caminha em direção às folhas pelo filho direito� cima → caminha em direção à raiz

66

Page 73: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando um zipper para árvores

Assim como o zipper de listas, o nosso zipper de árvoresprecisará guardar pelo menos o foco e o caminho percorrido.

1 data Arv a = ArvVazia | No (Arv a) a (Arv a)

1 type ZipperArv a =2 (Arv a, [Either (a, Arv a) (a, Arv a)])

67

Page 74: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando um zipper para árvores

1 toArvZipper :: Arv a -> ZipperArv a2 toArvZipper arv = (arv, [])3

4 fromArvZipper :: ZipperArv a -> Arv a5 fromArvZipper (arv, []) = arv6 fromArvZipper z = fromJust $ fromArvZipper <$> arvCima z7

8 arvFoco :: ZipperArv a -> Maybe a9 arvFoco (ArvVazia, _) = Nothing

10 arvFoco (No _ x _, _) = Just x11

12 arvTrocaFoco :: a -> ZipperArv a -> Maybe (ZipperArv a)13 arvTrocaFoco _ (ArvVazia, _) = Nothing14 arvTrocaFoco x (No a _ b, rastro) =15 Just (No a x b, rastro)

68

Page 75: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Movimentando o zipper

1 arvDir :: ZipperArv a -> Maybe (ZipperArv a)2 arvDir (ArvVazia, _) = Nothing3 arvDir (No e x d, rastro) =4 Just (d, Right (x, e):rastro)5

6 arvEsq :: ZipperArv a -> Maybe (ZipperArv a)7 arvEsq (ArvVazia, _) = Nothing8 arvEsq (No e x d, rastro) =9 Just (e, Left (x, d):rastro)

10

11 arvCima :: ZipperArv a -> Maybe (ZipperArv a)12 arvCima (_, []) = Nothing13 arvCima (arv, Left (x, d):rastro) =14 Just (No arv x d, rastro)15 arvCima (arv, Right (x, e):rastro) =16 Just (No e x arv, rastro)

69

Page 76: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Sobre o uso de Maybe

� Note que como no caso do zipper para listas, todas asoperações que podem falhar devolvem um Maybe a.

� Isto torna as nossas funções totais evitando, em tempo decompilação, uma série de erros que normalmenteocorreriam apenas em tempo de execução.

📝 Note

� Uma função é total se ela for definida para todos osvalores possíveis do seu tipo de entrada.

� Uma função é parcial se existem algumas entradas paraas quais ela não tem valor de saída definido.

1 > head []2 *** Exception: Prelude.head: empty list

70

Page 77: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Trocando valores - Versão na munheca

Tal qual fizemos com as listas, vamos trocar um valor por outroem nossa BST na unha e em seguida usando zippers!

1 trocaArv :: Ord a => a -> a -> Arv a -> Arv a2 trocaArv _ _ ArvVazia = ArvVazia3 trocaArv velho novo (No e x d)4 | x < velho = No e x $ trocaArv velho novo d5 | x > velho = No (trocaArv velho novo e) x d6 | otherwise = No e novo d

71

Page 78: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Trocando valores - Versão com zipper

1 trocaArvZ :: Ord a => a -> a -> Arv a -> Arv a2 trocaArvZ velho novo arv =3 fromArvZipper $ trocaArvZ' $ toArvZipper arv4 where5 trocaArvZ' z@(ArvVazia, _) = z6 trocaArvZ' z7 | f < velho = go arvDir8 | f > velho = go arvEsq9 | otherwise = fromJust $ arvTrocaFoco novo z

10 where11 f = fromJust $ arvFoco z12 go d = maybe z trocaArvZ' (d z)

72

Page 79: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Comentários finais

� Zippers foram propostos originalmente por Gérard Huetem 1997.

� Certamente muitos que trabalharam com árvores duranteos mais de 50 anos de linguagens funcionais fizerambuscas em largura.I Impressionantemente, um dos primeiros papers quedescrevem de maneira organizada e elegante como istopode ser feito é de 1993!

Veremos esta solução na próxima aula.I Em 2000 Chris Okasaki fez uma enquete com diversosusuários (em uma conferência da área) e descobriu quehavia um bloqueio mental generalizado. Em outraspalavras, ninguém sabia fazer isso direito em um contextofuncional!

73

Page 80: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Roseiras

Page 81: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Roseiras

� Roseiras (en: rose trees) são árvores que têm um númerovariável, potencialmente infinito3, de ramificações por nó.

3Veremos mais adiante como a avaliação preguiçosa nos permite trabalharcom EDs infinitas.

74

Page 82: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Roseira

15

3

1 4 8

42

17 22

24 25 26 27 28 29 30 31 32 33

43 44 88

75

Page 83: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando uma roseira (plantando? )

� Em Haskell podemos representar tal estrutura da seguintemaneira

1 data RoseTree a = EmptyRose | RoseTree a [RoseTree a]

� Contudo, a implementação acima permite que criemoscoisas assim:

1 arv = RoseTree 42 [EmptyRose]

� Não queremos isso! Temos algumas opções para evitar oproblema…

76

Page 84: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando uma roseira

1 data RoseTree a = EmptyRose | RoseTree a [RoseTree a]

1 Algoritmo do avestruz4: assumimos que o problema nãovai ocorrer pois a nossa implementação, assim como osusuários da nossa ED que são ”gente boa”, nunca vão fazera besteira de colocar uma árvore vazia na lista.

2 Função intermediária5: para acesso ao construtorRoseTree que verifica, em tempo de execução, que nãoestamos recebendo árvores vazias para incluir na lista.

3 Tipos fantasmas6: para garantir que tudo está correto emtempo de compilação!

4https://pt.wikipedia.org/wiki/Algoritmo_do_avestruz5https://wiki.haskell.org/Smart_constructors6https://wiki.haskell.org/Phantom_type 77

Page 85: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Uma pitada de type-safe programming com tipos fantasmas

� Um tipo fantasma (en: phantom type) é simplesmente umtipo paramétrico que não utiliza pelo menos um dos seustipos parâmetros em sua definição.

Talvez um exemplo seja ser mais esclarecedor…

78

Page 86: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Tipos fantasmas

1 data FormData a = FormData String

O exemplo parece estranho… Porque um tipo a se ele não éusado? De fato é possível usar o tipo FormData da seguintemaneira:

1 changeType :: FormData a -> FormData b2 changeType (FormData str) = FormData str

Que troca o tipo simplesmente chamando o mesmo construtordos dois lados da equação!

79

Page 87: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Usando tipos fantasmas

1 data Validated2 data Unvalidated3 -- Se escondermos os construtores dos usuários (basta4 -- não exportá-los) essa função é a única que pode5 -- ser usada pelos usuários. Ou seja, o usuário só6 -- pode criar FormData que não são validados.7 formData :: String -> FormData Unvalidated8 formData str = FormData str9

10 -- Podemos criar uma função que valida os dados11 validate :: FormData Unvalidated -> Maybe (FormData

Validated)↪→

12 validate (FormData str) = ...13

14 -- E assim GARANTIR que funções consumam apenas dados15 -- que foram validados!16 useData :: FormData Validated -> IO ()17 useData (FormData str) = ... 80

Page 88: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Dá pra melhorar!

1 data FormData a = FormData String

� Contudo, essa implementação não garante que só existamos tipos que façam sentido como FormData Validatede FormData Unvalidated.

� Tal como está, o código permitiria a criação de umFormData Int.I Claro, como só exportamos a função auxiliar formData,apenas a nossa própria biblioteca conseguiria tal feito.Mas nem isso quero deixar em aberto!

81

Page 89: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Dá pra melhorar!

Para resolver, podemos fazer7:

1 data ValidationStatus = Validated | Unvalidated2

3 data FormData (status :: ValidationStatus) where4 FormData :: String -> FormData status5

6 formData :: String -> FormData Unvalidated7 formData str = FormData str8

9 validate :: FormData Unvalidated -> Maybe (FormDataValidated)↪→

10 validate (FormData str) = ..

7Esse código exige o uso das extensões DataKinds, KindSignatures,GADTs

82

Page 90: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

E a roseira?

Ah sim! Estávamos falando de roseiras!

83

Page 91: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Tipos fantasmas na roseira

Utilizando tipos fantasmas, o código pode ser melhorado:

1 data Emptiness = Empty | NonEmpty2

3 data RoseTree (e :: Emptiness) a where4 EmptyRose :: RoseTree Empty a5 RoseTree :: a -> [RoseTree NonEmpty a] -> RoseTree

NonEmpty a↪→

💡 TipAqui vamos apenas arranhar a superfície do que é possívelfazer com type-safe programming e dependent types. Apesarde haver várias propostas em andamento para avançar alinguagem Haskell nesta direção, ela é atualmenteincompleta. Caso queira explorar além do que falaremos poraqui, procure pelas linguagens: Idris, Agda e Coq.

84

Page 92: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Um zipper para roseiras

Figura 2: craft-craft.net� Não faz mais sentido falar em direita ou esquerda para irem direção às folhas.

� Mais ainda, o que significa mudar o foco de uma roseira?� Temos uma mistura de um zipper de árvores com umzipper de listas! 85

Page 93: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando um zipper para roseiras

1 data RoseTreeZipper a = RoseTreeZipper2 a -- Valor do foco3 (ListZipper (RoseTree NonEmpty a)) -- Subzipper da

lista de filhos↪→

4 [(a, ListZipper (RoseTree NonEmpty a))] -- Rastro

Vamos comparar8 com o zipper de árvores e listas:

1 type ListZipper a = ([a], [a])

1 type ZipperArv a =2 (Arv a, [Either (a, Arv a) (a, Arv a)])

8Essa implementação não é minimal, mas facilita o entendimento.

86

Page 94: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando um zipper para roseiras

1 toRoseZipper :: RoseTree e a -> Maybe (RoseTreeZipper a)2 toRoseZipper EmptyRose = Nothing3 toRoseZipper (RoseTree v bs) =4 Just $ RoseTreeZipper v (toListZipper bs) []

1 roseFoco :: RoseTreeZipper a -> a2 roseFoco (RoseTreeZipper v _ _) = v

1 roseTrocaFoco :: RoseTreeZipper a-> a-> RoseTreeZipper a2 roseTrocaFoco (RoseTreeZipper _ lz ps) x =3 RoseTreeZipper x lz ps

87

Page 95: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando um zipper para roseiras

1 roseDir :: RoseTreeZipper a -> Maybe (RoseTreeZipper a)2 roseDir (RoseTreeZipper v lz ps) = do3 lz' <- lzDir lz4 Just $ RoseTreeZipper v lz' ps5

6 roseEsq :: RoseTreeZipper a -> Maybe (RoseTreeZipper a)7 roseEsq (RoseTreeZipper v lz ps) = do8 lz' <- lzEsq lz9 Just $ RoseTreeZipper v lz' ps

88

Page 96: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Implementando um zipper para roseiras

1 roseBaixo:: RoseTreeZipper a -> Maybe (RoseTreeZipper a)2 roseBaixo (RoseTreeZipper v lz ps) = do3 (RoseTree v' bs') <- lzFoco lz4 Just $ RoseTreeZipper v' (toListZipper bs') ((v, lz) :

ps)↪→

1 roseCima :: RoseTreeZipper a -> Maybe (RoseTreeZipper a)2 roseCima (RoseTreeZipper _ _ []) = Nothing3 roseCima (RoseTreeZipper _ _ ((v',lz'):ps)) =4 Just $ RoseTreeZipper v' lz' ps

89

Page 97: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Comentários finais

� Roseiras foram apresentadas por Lambert Meertens em1988.

� Sua variação preguiçosa para buscas vinculadas aotimização talvez seja a sua aplicação mais comumquando vinculada a zippers.

90

Page 98: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Exercício 3

A combinação das implementações das funções trocaFoco eroseCima, tal como está, descarta eventuais alterações feitasno valor em foco. Corrija a implementação.

1 data RoseTreeZipper a = RoseTreeZipper2 a -- Valor do foco3 (ListZipper (RoseTree NonEmpty a)) -- Subzipper da

lista de filhos↪→

4 [(a, ListZipper (RoseTree NonEmpty a))] -- Rastro

1 roseTrocaFoco :: RoseTreeZipper a-> a-> RoseTreeZipper a2 roseTrocaFoco (RoseTreeZipper _ lz ps) x =3 RoseTreeZipper x lz ps

1 roseCima :: RoseTreeZipper a -> Maybe (RoseTreeZipper a)2 roseCima (RoseTreeZipper _ _ []) = Nothing3 roseCima (RoseTreeZipper _ _ ((v',lz'):ps)) =4 Just $ RoseTreeZipper v' lz' ps 91

Page 99: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras

Page 100: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Balanceadas de Busca

� Árvores de busca com garantias de altura máxima.� Em outras palavras, árvores balanceadas em geral têm agarantia de que a sua altura é no máximo O(lgn).

� Árvores AVL e árvores rubro-negras são exemplos deárvores balanceadas de busca.

92

Page 101: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras

� Árvores Rubro-Negras são árvores de busca balanceadas.� Também chamadas de árvores vermelho-preto (en:red-black trees).

� Receberam este nome em um artigo de Guibas eSedgewick em 1978.I Supostamente pois a impressora apenas era capaz deimprimir vermelho e preto…

93

Page 102: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras

94

Page 103: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Características

� Uma árvore rubro-negra é uma árvore de busca binária,logo segue todas as regras:I todo nó da sub-árvore esquerda de um nó p tem chavemenor que a chave de p;

I todo nó da sub-árvore direita de um nó p tem chave maiorque a chave de p.

� Além disto, cada nó de uma árvore rubro negra tem asseguintes características:I Cor – vermelho ou preto.I Chave (ou valor) – Conteúdo do nó.I Dir, Esq – Sub-árvores direita e esquerda.

95

Page 104: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Regras de uma árvore rubro-negra

� Regra -1: É uma árvore binária de busca.� Regra 0: Os nós são vermelhos ou pretos.� Regra 1: A raiz é sempre preta.� Regra 2: Nenhum nó vermelho tem filhos vermelhos.� Regra 3: Os nós ”nulos” são considerados pretos.� Regra 4: Para cada nó p, todos os caminhos desde p até asfolhas contêm o mesmo número de nós pretos.

96

Page 105: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 1

7

5

4 6

9

8 10

É uma árvore rubro-negra?

Não! Viola a Regra 1 (A raiz é sempre preta).

97

Page 106: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 1

7

5

4 6

9

8 10

É uma árvore rubro-negra?

Não! Viola a Regra 1 (A raiz é sempre preta).

97

Page 107: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 2

14

5 37

28

25

74

63 82

É uma árvore rubro-negra?

Não! Viola a Regra 4 (Para cada nó p, todos os caminhos desdep até as folhas contêm o mesmo número de nós pretos).

98

Page 108: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 2

14

5 37

28

25

74

63 82

É uma árvore rubro-negra?

Não! Viola a Regra 4 (Para cada nó p, todos os caminhos desdep até as folhas contêm o mesmo número de nós pretos).

98

Page 109: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 3

14

5 37

28

25

74

63 82

É uma árvore rubro-negra?

Sim!

99

Page 110: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 3

14

5 37

28

25

74

63 82

É uma árvore rubro-negra?

Sim!

99

Page 111: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 4

14

5 37

28

25

74

63 82

É uma árvore rubro-negra?

Não! Viola a Regra 2 (Nenhum nó vermelho tem filhosvermelhos).

100

Page 112: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Árvores Rubro-Negras - Reconhecendo 4

14

5 37

28

25

74

63 82

É uma árvore rubro-negra?

Não! Viola a Regra 2 (Nenhum nó vermelho tem filhosvermelhos).

100

Page 113: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Representação

Por simplicidade, no restante das figuras vamos representar asárvores omitindo as folhas nulas (pretas). Uma árvore válidaseria então:

14

5 37

28

25

74

63 82

101

Page 114: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Ok, pra quê tudo isso?

� Restringindo a maneira que os nós podem ser coloridosdo caminho da raiz até qualquer uma das suas folhas, asárvores rubro-negras garantem:I que nenhum dos caminhos será maior que 2x ocomprimento de qualquer outro.

I que a árvore é aproximadamente balanceada.

14

5 37

28

25

74

63 82

102

Page 115: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Altura negra

A altura negra de um nó n é definida como o número de nóspretos (sem incluir o próprio n) visitados em qualquercaminho de n até as folhas.

� A altura negra do nó n é denotada por Hp(n).� Pela Regra 4, Hp(n) é bem definida para todos os nós daárvore.I A altura negra da árvore rubro-negra é definida comosendo a Hp(raiz).

14

5 37

28

25

74

63 82 103

Page 116: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Lema 1 - Altura máxima

� Lema 1: A altura máxima de uma árvore rubro-negra comn nós internos é de 2lg(n+ 1).I A prova pode ser feita por indução utilizando a Hp dos nósda árvore. Veja o Lema 13.1 do [CLRS] para a provacompleta.

� Corolário: As operações de Busca, Mínimo, Máximo,Sucessor e Predecessor podem ser efetuadas em tempoO(lg(n)).

104

Page 117: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Operações em árvores rubro-negras

� Busca – A busca que estamos acostumados funciona semmodificações.

� Inserção e Remoção – Se feitas sem qualquer cuidado,apesar de manter as propriedades de árvores binárias debusca, podem ferir as propriedades rubro-negras.

Veja animação de operações em:http://tommikaikkonen.github.io/rbtree

105

Page 118: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Bla bla bla… Show me the code!

Primeiro definimos as cores:

1 data RBColor = R | B

E em seguida, a árvore:

1 data RBTree a where2 RBEmpty :: Ord a => RBTree a3 RBT :: Ord a => RBColor -> RBTree a -> a -> RBTree a

-> RBTree a↪→

106

Page 119: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Operações básicas

1 data RBTree a where2 RBEmpty :: Ord a => RBTree a3 RBT :: Ord a => RBColor -> RBTree a -> a -> RBTree a

-> RBTree a↪→

1 empty :: Ord a => RBTree a2 empty = RBEmpty3

4 singleton :: Ord a => a -> RBTree a5 singleton x = RBT B empty x empty6

7 null :: RBTree a -> Bool8 null RBEmpty = True9 null _ = False

10

11 head :: RBTree p -> p12 head (RBT _ _ x _) = x13 head _ = error "head: empty Red Black Tree" 107

Page 120: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Busca e percurso ficam iguais…

1 data RBTree a where2 RBEmpty :: Ord a => RBTree a3 RBT :: Ord a => RBColor -> RBTree a -> a -> RBTree a

-> RBTree a↪→

1 elem :: a -> RBTree a -> Bool2 elem _ RBEmpty = False3 elem x (RBT _ l y r)4 | x < y = elem x l5 | x > y = elem x r6 | otherwise = True7

8 -- Percurso in-ordem9 toList :: RBTree a -> [a]

10 toList RBEmpty = []11 toList (RBT _ l v r) =12 toList l ++ v : toList r 108

Page 121: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores rubro-negras

11

2

1 7

5 8

14

15

4

109

Page 122: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores rubro-negras

11

2

1 7

5 8

14

15

4

110

Page 123: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores rubro-negras

11

2

1 7

5 8

14

15

4

111

Page 124: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores rubro-negras

11

2

1 7

5 8

14

15

4

112

Page 125: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores rubro-negras

11

2

1 7

5

4

8

14

15

113

Page 126: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção em árvores rubro-negras

11

2

1 7

5

4

8

14

15

Quebra a Regra 0 – Todo nó deve ser vermelho ou preto.

114

Page 127: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Consertando a Regra 0

� É preciso decidir, qual faz menos mal, colocar um nóvermelho ou um preto?I O vermelho pode não quebrar nada.I O preto vai desequilibrar a altura negra da raiz, comcerteza.

11

2

1 7

5

4

8

14

15

115

Page 128: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Consertando a Regra 0

� É preciso decidir, qual faz menos mal, colocar um nóvermelho ou um preto?I O vermelho pode não quebrar nada.I O preto vai desequilibrar a altura negra da raiz, comcerteza.

11

2

1 7

5

4

8

14

15

116

Page 129: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Mantendo as regras válidas

� Regra 0 resolvida, sempre insiro um nó com a cor vemelha.� E agora, qual regra eu quebrei?

I Melhor ainda, quais regras eu poderia ter quebrado?

11

2

1 7

5

4

8

14

15

117

Page 130: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Rebalanceamento

y

x

a b

z

c d

z

y

x

a b

c

d

Caso 1

z

x

a y

b c

d

Caso 2

x

a y

b z

c d

Caso 3

x

a z

y

b c

d

Caso 4118

Page 131: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Rebalanceamento

Primeiramente, criamos uma função que cria a árvore alvoapós o balanceamento:

y

x

a b

z

c d

1 buildRed :: Ord a => RBTree a -> a -> RBTree a -> a ->RBTree a -> a -> RBTree a -> RBTree a↪→

2 buildRed a x b y c z d =3 RBT R (RBT B a x b) y (RBT B c z d)

119

Page 132: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Rebalanceamento

Agora basta descrever as transições dos 4 casos:

z

y

x

a b

c

d

Caso 1

z

x

a y

b c

d

Caso 2

1 balance :: Ord a => RBColor -> RBTree a -> a -> RBTree a-> RBTree a↪→

2 balance B (RBT R (RBT R a x b) y c) z d = -- Caso 13 buildRed a x b y c z d4 balance B (RBT R a x (RBT R b y c)) z d = -- Caso 25 buildRed a x b y c z d 120

Page 133: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Rebalanceamento

x

a y

b z

c d

Caso 3

x

a z

y

b c

d

Caso 4

1 balance B a x (RBT R b y (RBT R c z d)) = -- Caso 32 buildRed a x b y c z d3 balance B a x (RBT R (RBT R b y c) z d) = -- Caso 44 buildRed a x b y c z d5 balance color a x b =6 RBT color a x b

121

Page 134: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção

� Fazemos a inserção como sempre.� Na linha 2, garantimos que a raiz continuará preta.� As chamadas à balance garantem que as propriedadesda árvore serão mantidas

1 insert :: Ord a => a -> RBTree a -> RBTree a2 insert x t = makeBlack $ ins t3 where4 ins RBEmpty = singleton x5 ins t2@(RBT color l y r)6 | x < y = balance color (ins l) y r7 | x > y = balance color l y (ins r)8 | otherwise = t29

10 makeBlack ~(RBT _ a y b) = RBT B a y b

122

Page 135: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Sobre a implementação

� Mesmo sem otimizações, esta é uma das implementaçõesfuncionais mais rápidas.I Os exercícios para casa tratam de possíveis otimizaçõesque fazem essa implemtação voar!

123

Page 136: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Comparação com a versão imperativa

📝 Note

� Se comparada à implementação imperativa ([CLRS], [SW]),a implementação funcional é bem mais simples pois usaalgumas transformações um pouco diferentes.

� Normalmente implementações imperativas dividem os 4casos apresentados aqui em 8 casos de acordo com a cordo irmão do nó vermelho com filho vermelho.

� Saber a cor do ”tio”do nó vermelho permite, em algunscasos, utilizar menos atribuições ou terminar orebalanceamento sem precisar propagá-lo até a raiz.

� Tais otimizações em uma implementação persistente sãoirrelevantes! Precisamos de qualquer forma copiar todo ocaminho até a raiz, então não há razão de utilizar astransformações mais complicadas! 124

Page 137: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Atenção

� DangerA implementação da inserção que apresentamos tem umerro grave! Você é capaz de apontá-lo?

1 insert :: Ord a => a -> RBTree a -> RBTree a2 insert x t = makeBlack $ ins t3 where4 ins RBEmpty = singleton x5 ins t2@(RBT color l y r)6 | x < y = balance color (ins l) y r7 | x > y = balance color l y (ins r)8 | otherwise = t29

10 makeBlack ~(RBT _ a y b) = RBT B a y b

125

Page 138: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Corrigindo a inserção

� A correção é simples, bastando fazer com que o nóinserido seja vermelho.

1 insert :: Ord a => a -> RBTree a -> RBTree a2 insert x t = makeBlack $ ins t3 where4 ins RBEmpty = RBT R empty x empty5 ins t2@(RBT color l y r)6 | x < y = balance color (ins l) y r7 | x > y = balance color l y (ins r)8 | otherwise = t29

10 makeBlack ~(RBT _ a y b) = RBT B a y b

� Quais outros erros poderiam estar escondidos na nossaimplementação? O compilador não poderia nos ajudar adetectá-los? 126

Page 139: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Comentários finais

� A versão de árvore rubro-negra que apresentamos aqui édevida a Chris Okasaki e foi apresentada em 1999. Antesdisso as implementações funcionais eram uma adaptaçãoforçada das implementações imperativas.

� Aqueles mais atentos vão perceber que houve a omissãode um tópico essencial: remoção funcional de nós.I Essa omissão também ocorre no livro do Okasaki [CO].I Assim como a inserção, a versão funcional ”redonda”deárvores rubro-negras demorou para aparecer. Emparticular, a remoção só apareceu em 2014 em um paperapropriamente chamado (tradução minha): ”Remoções: amaldição das árvores rubro negras”.

127

Page 140: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Type-safe Red-Black Trees

Page 141: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Type-safe Red-Black Trees

� Assim como fizemos com as Roseiras, vamos melhorar onosso código para que erros como o que cometemos (ealguns outros) não sejam possíveis.

� Para isto vamos precisar relembrar um pouco dematemática.

128

Page 142: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Axiomas de Peano

� Em 1879, Giuseppe Peanoapresentou umafundamentação (utilizandoa linguagem da época) queadmite três conceitosprimitivos (númeronatural, zero e sucessor)relacionados entre si porcinco axiomas.

� Este conjunto de axiomasé a base para aformalização ordinal denúmeros naturais.

� Mais detalhes em [MC].Figura 3: Giuseppe Peano

129

Page 143: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Os axiomas de Peano

� Indicaremos por σ(n) o sucessor de n e, como usual, 0para denotar o valor zero.

� Os cinco axiomas são:1 0 é um número natural.2 Todo número natural n tem um sucessor σ(n).3 0 não é sucessor de nenhum número.4 Se σ(n) = σ(m) então n = m.5 Princípio da indução completa: Seja S um conjunto denúmeros naturais tal que: (i) 0 ∈ S; e (ii) Se n ∈ S entãoσ(n) ∈ S; então S é o conjunto de todos os númerosnaturais.

130

Page 144: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Cálculo λ

� Criado por Alonzo Church na décadade 1930.I Apresentado em 1932 e refinadoaté 1940 quando apresentou a suaversão tipada.

I Church foi o orientador dedoutorado do Alan Turing, quepublicou o paper descrevendomáquinas de Turing em 1936.

I Para saber mais sobre Cálculo-λ:aqui e [SK] Cap. 5. Figura 4: Alonzo Church

131

Page 145: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Cálculo λ e Axiomas de Peano

� Church apresentou uma maneira de representar inteirosem Cálculo-λ utilizando funções anônimas.

� Essa representação ficou conhecida como Números deChurch.

� Um número n é codificado como a chamada de umafunção n vezes:

1 ZERO = \f x -> x2 UM = \f x -> f x3 DOIS = \f x -> f (f x)4 TRES = \f x -> f (f (f x))

� Que nada mais é que a ideia da função σ apresentada porPeano!

132

Page 146: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Saindo de valores e indo para tipos

� A implementação sugerida anteriormente é baseada emvalores disponíveis apenas em tempo de execução.

� Queremos usar essa garantia em tempo de compilação!9.

1 -- Inteiros de Peano2 data Peano = Zero | Succ Peano

E os números naturais passam a ser…

1 type One = Succ Zero2 type Two = Succ One3 type Three = Succ Two4 ...

9Essa implementação usa a extensão DataKinds133

Page 147: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Pronto! Já temos o que precisamos!

� Vamos relembrar as regras de uma árvore rubro-negra:I Regra 0: Os nós são vermelhos ou pretos ✗I Regra 1: A raiz sempre é preta ✗I Regra 2: Nenhum nó vermelho tem filhos vermelhos ✗I Regra 3: Os nós nulos são pretos ✗I Regra 4: Altura negra à esquerda e à direita iguais ✗

… e nossa implementação atual:

1 data RBColor = R | B

1 data RBTree a where2 RBEmpty :: Ord a => RBTree a3 RBT :: Ord a => RBColor -> RBTree a -> a -> RBTree a

-> RBTree a↪→

134

Page 148: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Vamos começar pelo mais fácil… Regras 0 e 3

� Regra 0: Os nós são vermelhos ou pretos ✓� Regra 1: A raiz sempre é preta ✗� Regra 2: Nenhum nó vermelho tem filhos vermelhos ✗� Regra 3: Os nós nulos são pretos ✓� Regra 4: Altura negra à esquerda e à direita iguais ✗

1 data Color = R | B2 type Black = Node B -- Apenas um atalho3 type Red = Node R4

5 data Node (c :: Color) a where -- regra 06 Null :: Ord a => Black a -- regra 37 RBT :: Ord a => c -> Node c0 a -> a -> Node c1 a ->

Node c a↪→

135

Page 149: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Regra 1

� Regra 0: Os nós são vermelhos ou pretos ✓� Regra 1: A raiz sempre é preta ✓� Regra 2: Nenhum nó vermelho tem filhos vermelhos ✗� Regra 3: Os nós nulos são pretos ✓� Regra 4: Altura negra à esquerda e à direita iguais ✗

1 data Color = R | B2 type Black = Node B3 type Red = Node R4

5 data RBTree a = Black a -- regra 16

7 data Node (c :: Color) a where -- regra 08 Null :: Ord a => Black a -- regra 39 RBT :: Ord a => c -> Node c0 a -> a -> Node c1 a ->

Node c a↪→136

Page 150: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Regra 2

� Regra 0: Os nós são vermelhos ou pretos ✓� Regra 1: A raiz sempre é preta ✓� Regra 2: Nenhum nó vermelho tem filhos vermelhos ✓� Regra 3: Os nós nulos são pretos ✓� Regra 4: Altura negra à esquerda e à direita iguais ✗

1 data Color = R | B2 type Black = Node B3 type Red = Node R4

5 data RBTree a = Black a -- regra 16

7 data Node (c :: Color) a where -- regra 08 Null :: Ord a => Black a -- regra 39 Black::Ord a => Node c0 a -> a -> Node c1 a -> Black a

10 -- regra 211 Red :: Ord a => Black a -> a -> Black a -> Red a 137

Page 151: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Regra 4

� Regra 0: Os nós são vermelhos ou pretos ✓� Regra 1: A raiz sempre é preta ✓� Regra 2: Nenhum nó vermelho tem filhos vermelhos ✓� Regra 3: Os nós nulos são pretos ✓� Regra 4: Altura negra à esquerda e à direita iguais ✓

1 data Peano = Zero | Succ Peano2 type One = Succ Zero3

4 data RBTree a = forall n. T (Black n a) -- regra 15

6 data Node (c :: Color) (n :: Peano) a where -- regra 07 Null :: Ord a => Black One a -- regra 3, 48 Black :: Ord a => Node c0 n a -> a -> Node c1 n a ->

Black (Succ n) a -- regra 4↪→

9 -- regra 2 e 410 Red :: Ord a => Black n a -> a -> Black n a -> Red n a 138

Page 152: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

gRReat success!

#SQN

139

Page 153: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Busca

A busca precisa apenas de algumas pequenas alterações parafuncionar na nova ED:

1 elem :: Ord a => a -> RBTree a -> Bool2 elem x (T node) = elemNode x node3

4 elemNode :: a -> Node c n a -> Bool5 elemNode x node =6 case node of7 Null -> False8 (Black l y r) -> elem' x l y r9 (Red l y r) -> elem' x l y r

10 where11 elem' e l y r12 | e < y = elemNode e l13 | e > y = elemNode e r14 | otherwise = True 140

Page 154: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção

� A estratégia que utilizamos antes de:I Inserir um novo nó vermelho.I Verificar se há um problema e propagar as correções até araiz…

� … não funciona mais!� Os tipos proibem que criemos uma árvore inválida mesmoque temporariamente!

� Precisamos montar uma árvore válida diretamente.

141

Page 155: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Relembrando o rebalanceamento

142

Page 156: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Consertando a inserção

� A ideia da implementação se baseia na seguinteobservação: a inserção de um novo nó vermelho abaixode…I … um nó vermelho pode causar uma violação das regras.I … um nó preto não gera uma violação das regras.

� Assim, dividimos a implementação da inserção em cores!

143

Page 157: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

O tipo Violation

1 data Violation (n :: Peano) a where2 Case14 -- Nós vermelhos à esq. esq.3 :: Ord a => -- Caso 1 Caso 44 a -- Red y Red z5 -> a -- Red x Red y6 -> Black n a -- a b7 -> Black n a -- b c8 -> Black n a -- c d9 -> Violation n a

10 Case23 -- Nós vermelhos à dir. dir.11 :: Ord a => -- Caso 2 Caso 312 a -- Red x Red y13 -> a -- Red y Red z14 -> Black n a -- a b15 -> Black n a -- b c16 -> Black n a -- c d17 -> Violation n a 144

Page 158: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção abaixo de um nó vermelho

1 -- insere o elemento x0 na árvore de raiz vermelha n2 -- Fica sob responsabilidade do pai a correção de3 -- eventuais violações.4 insR :: a -> Red n a -> Either (Violation n a) (Red n a)5 insR x0 n@(Red l0 y0 r0) -- l0 e r0 são pretos6 | x0 < y0 =7 case insB x0 l0 of8 (Left black) -> mkRed black y0 r09 (Right (Red a x b)) -> Left $ Case14 y0 x a b r0

10 | x0 > y0 =11 case insB x0 r0 of12 (Left black) -> mkRed l0 y0 black13 (Right (Red b y c)) -> Left $ Case23 y0 y l0 b c14 | otherwise = Right n15 where16 mkRed a v c = Right $ Red a v c

145

Page 159: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção abaixo de um nó preto i

1 -- insere o elemento x0 na árvore de raiz negra e2 -- devolve um novo no pode ser vermelho ou negro3 insB :: a -> Black n a -> Either (Black n a) (Red n a)4 insB x0 Null = Right $ Red Null x0 Null5 insB x0 n@(Black l0 y0 r0)6 | x0 < y0 =7 case l0 of8 Null -> eitherInsBL x0 Null y0 r09 black@Black{} -> eitherInsBL x0 black y0 r0

10 red@Red{} ->11 case insR x0 red of12 (Left (Case14 y x a b c)) -> -- Caso 113 balance a x b y c y0 r014 (Left (Case23 x y a b c)) -> -- Caso 215 balance a x b y c y0 r016 (Right r) ->17 mkBlack y0 r r0

146

Page 160: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção abaixo de um nó preto ii

18

19 -- O caso de x0 > y0 é semelhante, porém pode causar20 -- as violações 3 ou 421 | x0 > y0 =22 case r0 of23 Null -> eitherInsBR x0 l0 y0 Null24 black@Black{} -> eitherInsBR x0 l0 y0 black25 red@Red{} ->26 case insR x0 red of27 (Left (Case14 z y b c d)) -> -- Caso 428 balance l0 y0 b y c z d29 (Left (Case23 y z b c d)) -> -- Caso 330 balance l0 y0 b y c z d31 (Right r) ->32 mkBlack y0 l0 r33

34 -- Neste caso o valor já estava na árvore35 | otherwise = Left n

147

Page 161: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção abaixo de um nó preto iii

36 where37 mkBlack x a b = Left $ Black a x b38 eitherInsBR x l y r = either (mkBlack y l) (mkBlack y l)

(insB x r)↪→

39 eitherInsBL x l y r = either (flip (mkBlack y) r) (flip(mkBlack y) r) (insB x l)↪→

40 balance a x b y c z d = Right $ Red (Black a x b) y (Blackc z d)↪→

148

Page 162: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Inserção geral

E finalmente a inserção na árvore propriamente dita

1 -- insere o elemento x na árvore t2 insert :: a -> RBTree a -> RBTree a3 insert x (T node) =4 -- pela regra 1 (e o tipo de RBTree) sabemos que node5 -- é preto, então insB type-checks6 case insB x node of7 (Left b) -> T b8 (Right (Red l v r)) -> T $ Black l v r

149

Page 163: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Agora sim!

Quase!

150

Page 164: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Ainda falta uma regra

� Regra -1: A árvore é de busca ✗� Infelizmente, para fazer a verificação em tempo decompilação, precisamos de uma linguagem com tiposdependentes (en: dependent types) como Idris.I Reveja o comentário quando falamos sobre este assuntorelacionado às Roseiras.

� O máximo que podemos fazer, enquanto as propostas detipos dependentes para Haskell ainda não estão prontas,é uma verificação em tempo execução 😢

151

Page 165: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Comentários finais

� Implementações type-safe (com diversos graus de safety) de árvores rubro-negras abundam na Internet.I Inclusive em linguagens como Java.

� Dentre as implementações em linguagens funcionais, asseguintes características de implementação comuns à quemostramos aqui estão presentes (em maior ou menorgrau) em muitas delas:I Uso de GADTs e DataKinds para garantir as cores.I Números de Peano para assegurar que a altura negra estáconsistente.

I Divisão entre inserções vermelhas e inserções pretas.� Até onde pude averiguar, contudo, o uso de um tipoViolation como o que fizemos aqui para melhororganização e entendimento do código é minhajaboticaba.

152

Page 166: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Referências

Page 167: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Livros

� [CO]� Purely Functional DataStructuresI Por Chris Okasaki

153

Page 168: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Livros

� [CLRS]� Introduction to Algorithms

I Por Thomas H. Cormen& Charles E. Leiserson &Ronald L. Rives/t &/Clifford Stein

154

Page 169: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Livros

� [SW]� Algorithms

I Por Robert Sedgewick &Kevin Wayne

155

Page 170: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Livros

� [MC]� Números - Umaintrodução à MatemáticaI Por César Polcino Milies& Sônia Pitta Coelho

156

Page 171: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Livros

� [SK]� Formal Syntax andSemantics of ProgrammingLanguages: A LaboratoryBased ApproachI Por Kenneth Slonneger& Barry L. Kurtz

� Disponível gratuitamenteaqui:http://homepage.divms.uiowa.edu/~slonnegr/plf/Book/

157

Page 172: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Artigos - Varredura de árvores

� Okasaki, Chris. ”Breadth-first numbering: lessons from asmall exercise in algorithm design.”InternationalConference on Functional Programming: Proceedings ofthe fifth ACM SIGPLAN international conference onFunctional programming. 2000.

� Jones, Geraint, and Jeremy Gibbons. ”Linear-time BreadthFirst Tree Algorithms: An exercise in the arithmetic of foldsand zips”. Department of Computer Science, The Universityof Auckland, New Zealand, 1993.

158

Page 173: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Artigos - Árvores rubro-negras

� Okasaki, Chris. ”Red-black trees in a functional setting.”Journal of functional programming 9.4 (1999): 471-477.

� Germane, Kimball, and Matthew Might. ”Deletion: Thecurse of the red-black tree.” Journal of FunctionalProgramming 24.4 (2014): 423-433.

� Kahrs, Stefan. ”Type-Safe Red-Black Trees with JavaGenerics.” Specification Transformation Navigation (2009):168.

159

Page 174: Estruturas de dados puramente funcionais - Dia 1pesquisa.ufabc.edu.br/haskell/cursos/19.q3.eds_funcionais/files/dia0… · Outline 1 Introdução 2 Estruturasfuncionaisvs.estruturasimperativas

Outras Referências

� Huet, Gérard. ”The zipper.” Journal of functionalprogramming 7.5 (1997): 549-554.

� Meertens, Lambert. ”First steps towards the theory of rosetrees.”CWI, Amsterdam (1988).

� Eisenberg, Richard A. ”Dependent types in haskell: Theoryand practice.”. PhD Thesis, University of Pensylvania 2016.

160