180 likes | 314 Views
Árvores com valores em todos os nós.
E N D
Árvores com valores em todos os nós Existe também a função flatten que retorna a lista de valores que estão nos nós da árvores. A ordem pela qual este valores são compostos é, usualmente chamada, de inorder. Ou seja, o valor do nó de topo é colocado entre as listas correspondentes aos valores dos nós das sub-árvores esquerda e direita, que são ordenadas de forma similar. fun flatten Empty = [ ] | flatten (Tr (t1, a, t2)) = flatten t1 @ [a] @ flatten t2 Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Árvores com valores em todos os nós Se pretendermos uma ordem diferente para os valores da lista podemos definir as funções: fun preorder Empty = [ ] | preorder (Tr (t1, a, t2)) = [a] @ preorder t1 @ preorder t2 fun postorder Empty = [ ] | postorder (Tr (t1, a, t2)) = postorder t1 @ postorder t2 @ [a] Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Árvores com valores em todos os nós Podemos introduzir ainda uma função genérica sobre árvores (análoga a btreeop). Esta função recebe como argumento um valor constante, uma função g e uma árvore. A constante é retornada no caso da árvore ser Empty, caso contrário, a função g é usada para combinar os resultados do processamento recursivo das sub-árvores esquerda e direita: fun treeop c g Empty = c | treeop c g (Tr (t1, a, t2)) = g (treeop c g t1, a, treeop c g t2) Ao contrário da função btreeop, neste caso decidiu-se que a função g não está na forma curried, recebendo assim um triplo de argumentos. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Árvores com valores em todos os nós Usualmente estas árvores são usadas quando se pretende manter uma colecção de items ordenados e o uso de uma lista torna as inserções muito ‘caras’ e consequentemente ineficientes. Podemos comprovar este facto pela análise da implementação de um algoritmo de ordenação de árvores, que é usado para ordenar listas. O algoritmo é semelhante a uma mistura do quicksort com o insert sort, onde é usada uma árvore para organizar o posicionamento dos elementos. À medida que a árvore é percorrida, os elementos são inseridos na posição correcta e assim, quando a árvore é achatada (flatten) a lista resultante está ordenada. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Árvores com valores em todos os nós fun treeinsert Empty m = leaf m | treeinsert (Tr (t1, n, t2)) m = if (lessthan n) m (i.e. m<n) then Tr (treeinsert t1 m, n, t2) else Tr (t1, n, treeinsert t2 m) fun treesort x = flatten (accumulate treeinsert Empty x) A ordenação funciona da seguinte forma: começa por receber uma árvore vazia vai acumulando os elementos na árvore, através da função treeinsert, com cada elemento da lista recebida como argumento, de cada vez. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Árvores com valores em todos os nós As árvores intermédias estão sempre ordenadas porque se começa a partir da lista vazia, usando apenas a função treeinsert que preserva a ordem. A lista final é obtida simplesmente por achatamento da árvore assim obtida. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas Introduz-se agora uma notação para listas que não é standard ML. Essa notação é chamada ZF e permite denotar de forma sucinta aquelas listas que resultam da aplicação encadeada das funções map, link e filter, embora tenha correspondência na notação standard. Um caso em que esta notação se torna muito conveniente é o da função permutations que será apresentada como exemplo. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas A forma mais simples que a notação ZF pode assumir é: [E | V1 E1] em que E1 é uma expressão do tipo lista onde a variável V1 toma valores. Por sua vez E é uma expressão em que V1 pode ocorrer. Para cada valor possível para V1 tem-se o valor correspondente para E. Ao componente da forma V1 E1 dá-se o nome de gerador uma vez que este é usado para gerar a lista de valores para V1 a partir da lista denotada por E1. A expressão completa é chamada de lista em compreensão, mas a notação é também conhecida por notação ZF, que vem do nome Zermelo-Fraenkel. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas Um exemplo da notação ZF será: [2*x | x 1 upto 5] que denota a lista [2*1, 2*2, 2*3, 2*4, 2*5] = [2, 4, 6, 8, 10] Podemos também definir a função sum usando a notação ZF. A função sum recebe uma função g e soma os valores g(y), em que y varia de 0 a x. fun sum g x = sumlist [g y | y 0 upto x] Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas A notação ZF pode ser generalizada da seguinte forma: [E | V1 E1; V2 E2; ...; Vn En] Aqui cada uma das variáveis locais Vi toma valores na lista que lhe corresponde Ei e E pode envolver ocorrências de todos os Vis. Além disso cada Ei pode envolver ocorrências dos Vjs precedentes (1 j < i). Assim, o verdadeiro significado é que Ei pode depender dos valores tomados por V1, V2, ..., Vi-1. Por exemplo [x* y | x 1 upto 20; y 1 upto x] denota a lista [1*1, 2*1, 2*2, ..., 20*1, ..., 20*20] Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas Para encontrar a lista denotada por expressões do tipo indicado basta fixar uma a uma as variáveis V1, V2, ... fazendo variar as de índice mais elevado. Eis um exemplo que se torna muito difícil de expressar se não usarmos a notação ZF. A função permutations calcula todas as permutações dos elementos de uma lista, retornando uma lista de listas. Faz-se ainda uso de uma função infixa - - que remove a primeira ocorrência de um dado elemento de uma lista. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas fun permutations [] = [[]] | permutations x = [a::y | a x, y permutations (x - - a)] infix - - fun [] - - a = [] | (a::x) - - a = x | (b::x) - - a = b::(x - - a) - - : = list X = = list permutations: = list (= list) list Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas permutations: = list (= list) list - - : = list X = = list A lista de permutações de uma lista vazia é [[]], uma vez que [] é uma permutação da lista vazia. Para uma lista não vazia x, as permutações são calculadas como todas as listas da forma a::y onde a é um elemento de x e y é uma permutação de x tendo sido removido o elemento a. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas Outra extensão útil da notação ZF é permitir condições para qualificar um gerador para que os elementos da lista gerada que não satisfaçam a condição sejam filtrados. Assim um gerador pode ter a forma Vi Ei; B onde a condição B será uma expressão booleana, envolvendo algumas variáveis dos geradores precedentes (V1, ..., Vi). Por exemplo fun ordcart x y = [(a,b) | a x; b y; a < b] ordcart: int list int list (int x int) list define a lista de pares da forma (a, b), tal que a vem de x, b vem de y e a < b. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas Para traduzir (expandir) listas na notação ZF para ML, basta compreender o significado da referida notação. Assim, uma possível correspondente em ML da expressão [E | V1 E1] é dada por map (fn V1 E) E1 que não é mais do que a lista de possíveis valores que E toma quando o parâmetro da função anónima, V1, é substituído pelos valores da lista E1 via map. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas Generalizando, para encontrar a correspondente em ML da expressão [E | V1 E1; V2 E2; ...; Vn En] basta notar que esta expressão é quase equivalente a (fixando V1) [[E | V2 E2; ...; Vn En] | V1 E1] mas como esta expressão forma uma lista de listas, se utilizarmos a função link obtemos uma expressão exactamente equivalente à primeira: link [[E | V2 E2; ...; Vn En] | V1 E1] Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas E, fazendo uso da primeira regra enunciada tem-se link (map (fn V1 [E | V2 E2; ...; Vn En]) E1) Por último, a tradução de [E | V1 E1; V2 E2; ...; Vn En; B] é dada, naturalmente por filter B’ [E | V1 E1; V2 E2; ...; Vn En] em que B’ representa o predicado contido em B. Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)
Notação ZF para listas No caso do exemplo apresentado [(a,b) | a x; b y; a < b] em que B é a < b,B’ será o predicado par_ordenado definido da seguinte forma fun par_ordenado (x, y) = x < y e teremos a seguinte correspondência filter par_ordenado [(a,b) | a x; b y] Programação em Lógica e Funcional (2000/01) (Actualizado em 2004/05)