elsa carvalho 72 universidade da madeira departamento de matemática e engenharias programação em...
TRANSCRIPT
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
1Universidade da Madeira
Departamento de Matemática e Engenharias
Certas abstracções, como por exemplo, imagens, datas, cartas de jogar, etc., são concretizadas, em linguagens que não suportam definição de tipos de raiz, com base em tipos pré-definidos na linguagem.Em Pascal, por exemplo, teremos que nos restringir aos construtores, record, enum e array e aos tipos primitivos integer, char, etc.. Assim, para representar anos (1991, 1992, ...) podemos por exemplo escolher o tipo 0..3000. O compilador deixará passar ‘erros’ como ‘somar’ anos (1991 + 1992) operação que não faz sentido neste contexto.
Definição de novos tipos
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
2Universidade da Madeira
Departamento de Matemática e Engenharias
Definição de novos tiposResumindo: em sistemas de tipos que não suportem directamente as abstracções que queremos representar, o melhor a que podemos aspirar é a uma representação indirecta recorrendo a tipos e construções de tipos disponíveis, sujeitos a que o type checker interprete ‘erradamente’ os nossos conceitos.
O ML ultrapassa estas limitações e possibilita a definição de tipos inteiramente novos. Isso irá ser mostrado de seguida, começando pelas formas mais simples.
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
3Universidade da Madeira
Departamento de Matemática e Engenharias
Estes tipos são equivalentes aos tipos de enumeração finita encontrados noutras linguagens (como seja o Pascal) e são introduzidos através da enumeração de constantes, como se vê de seguida.
A palavra chave datatype indica a definição de um novo tipo, segue-se o nome do tipo, o símbolo “=” e as constantes (operações construtoras) separadas por “|”.
datatype direction = Up | Down | Left | Right
Esta definição introduz quatro novos valores distintos, cujo tipo é direction:
Up: direction Down: direction Left: direction Right: direction
Tipos com construtoras constantes
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
4Universidade da Madeira
Departamento de Matemática e Engenharias
Estes são os únicos valores definidos para este tipo.
O novo tipo pode ser combinado com tipos existentes (de uma forma apropriada) e, como tal, podemos escrever expressões tal como:
[Up, Down, Up, Up, Right]
que é um valor do tipo direction list.
No entanto, se tentarmos escrever [Right, 3] ser-nos-à indicado um erro de tipos, uma vez que uma lista só pode ser composta por elementos de um mesmo tipo. O que não é o caso:
Right: direction
3: integer
Tipos com construtoras constantes
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
5Universidade da Madeira
Departamento de Matemática e Engenharias
Temos outros exemplos, como sejam:
datatype cardvalue = Two | Three | ... | Queen | King | Ace
datatype suit = Spades | Diamonds | Hearts | Clubs
Qualquer tipo enumerado terá, automaticamente, o operador de igualdade definido, o que permite que valores desse tipo sejam comparados com =.
Assim Up = Down é uma expressão válida, do tipo bool e com valor false.
Tipos com construtoras constantes
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
6Universidade da Madeira
Departamento de Matemática e Engenharias
Tipos com construtoras constantes
As novas constantes, introduzidas através da definição de tipos, também podem ser utilizadas no ‘pattern matching’, o que nos permite escrever funções, com argumentos do novo tipo, por análise de casos.
Eis um exemplo
fun clockwise Up = Right| clockwise Right = Down| clockwise Down = Left| clockwise Left = Up
Daqui podemos extrapolar que o tipo desta função é:
clockwise: direction direction
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
7Universidade da Madeira
Departamento de Matemática e Engenharias
Tipos com construtoras paramétricas
Vamos agora introduzir uma ligeira generalização que nos permite introduzir novos tipos com construtores que não são constantes.
A diferença entre esta forma de definir tipos e a anterior está na possibilidade de introduzir parâmetros nas construtoras. Há que mencionar o tipo do parâmetro: a seguir ao nome da operação coloca-se a palavra chave of e depois o nome ou construção de um tipo. Por exemplo
datatype card = Card of (cardvalue X suit)
Neste caso card é o nome de um novo tipo e Card é uma construtora de valores do tipo card.
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
8Universidade da Madeira
Departamento de Matemática e Engenharias
Tipos com construtoras paramétricas
O tipo da construtora é:
Card: (cardvalue X suit) card
ou seja, pode ser aplicado a um argumento do tipo cardvalue x suit, produzindo um valor do tipo card.
Assim, as seguintes expressões, são válidas:
Card (Two, Diamonds)Card (King, Clubs)
O teste implícito de igualdade estende-se a estes tipos. Intuitivamente Card (Queen, Spades) = Card (Queen, Diamonds) tem o valor false, pois Spades = Diamonds tem o valor false.
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
9Universidade da Madeira
Departamento de Matemática e Engenharias
Também neste caso as construtoras continuam a ser a forma de constituir padrões em parâmetros de funções acessórias.
Por exemplo
fun suitof (Card (v, s)) = sfun valueof (Card (v,s)) = vfun isheart(Card (cv, Heart)) = true| isheart(Card (cv, s)) = false
Temos assim dois selectores e um predicado, cujos tipos podemos definir:
suitof: card suitvalueof: card cardvalueisheart: card bool
Tipos com construtoras paramétricas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
10Universidade da Madeira
Departamento de Matemática e Engenharias
É boa técnica de programação usar novos tipos mesmo quando existe na linguagem um tipo adequado para representar os valores pretendidos. Desta forma temos a garantia de uma verificação de tipos mais eficaz.Por exemplo, se queremos realizar cálculos com as idades e alturas de pessoas, representados por inteiros, podemos definir dois novos tipos:
datatype altura = Alt of intdatatype idade = Idade of int
Assim as alturas terão a forma Alt (n) e as idades serão representadas por Idade (n), onde n: int.
Tipos com construtoras paramétricas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
11Universidade da Madeira
Departamento de Matemática e Engenharias
As construtorasAlt: int alturaIdade: int idade
realizam as conversões apropriadas entre inteiros e os novos tipos. Isto torna-se vantajoso, uma vez que, embora seja possível somar inteiros, essa não é uma operação comum entre idades e alturas (por exemplo).
Com a introdução destes tipos, a operação Alt 190 + Idade 30 dará origem a um erro, pelo verificador de tipos.
Tipos com construtoras paramétricas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
12Universidade da Madeira
Departamento de Matemática e Engenharias
Suponhamos que se pretende introduzir um tipo autor e um tipo livro. Os autores são usualmente representados por strings (o nome), mas pretende-se um caso especial para um autor anónimo - ‘Anon’.De igual modo um livro será representado pelo seu título (uma string) e por uma lista de autores, mas pretende-se distinguir entre livros de capa dura e livros de capa ‘mole’.Uma forma de o fazer é definir:
datatype autor = Autor of string | Anon
datatype livro = Paperback of (string x autor list) | Hardback of (string x autor list)
Tipos com construtoras alternativas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
13Universidade da Madeira
Departamento de Matemática e Engenharias
Estas definições introduzem os tipos autor e livro, bem como as quatro construtoras
Autor: string autorAnon: autorPaperback: (string x autor list) bookHardback: (string x autor list) book
Desta forma é possível criar objectos do tipo livro e autor de diferentes maneiras:
val estelivro = Hardback (“Elements of Functional Programming”, [Autor “C.M.P.Read”])
val outrolivro = Paperback (“O Desconhecido”, [Anon])
Tipos com construtoras alternativas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
14Universidade da Madeira
Departamento de Matemática e Engenharias
Correspondentemente, funções definidas por emparelhamento de padrões (‘pattern matching’) com os tipos livro e autor terão de ter mais do que um caso:
fun titulo (Paperback (s, al)) = s| titulo (Hardback (s, al)) = s
fun listaautores (Paperback (s, al)) = al| listaautores (Hardback (s, al)) = al
fun capamole (Paperback (s, al)) = true| capamole (Hardback (s, al)) = false
Tipos com construtoras alternativas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
15Universidade da Madeira
Departamento de Matemática e Engenharias
Note-se que novos tipos são, essencialmente, uniões disjuntas dos tipos componentes que aparecem como argumentos das construtoras. Além disso, podemos pensar nas construtoras, como funções que distinguem os valores dessa união para que se saiba qual o tipo componente a que pertencem. Para ilustrar essa ideia vai-se construir um tipo mix, que é a união (disjunta) dos tipos int e string:
datatype mix = Int of int | Str of string
Tipos com construtoras alternativas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
16Universidade da Madeira
Departamento de Matemática e Engenharias
Podemos pensar nas construtorasInt: int mixStr: string mix
como etiquetas para os inteiros/strings de forma a constituírem valores do tipo mix.
Uma lista mista de inteiros e strings pode agora ser construída dentro do sistema de tipos com o tipo mix list:
val mix1 = [Str “ola”, Int 3, Str “hello”, Int 5, Int 8]
Tipos com construtoras alternativas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
17Universidade da Madeira
Departamento de Matemática e Engenharias
Além disso, podemos extrair, dessa lista, os inteiros (por exemplo) sem que estes se confundam com os valores do tipo string:
fun getints [] = []| getints(Int n::x) = n::getints x| getints(Str s::x) = getints x
Temos que:
mix1: mix listgetints: mix list int list
egetints mix1 = [3, 5, 8]
Tipos com construtoras alternativas
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
18Universidade da Madeira
Departamento de Matemática e Engenharias
Assume-se que a definição de tipos pode ser recursiva e, desta forma, o novo tipo pode aparecer como parte do argumento de uma construtora desse mesmo tipo.
Por exemplo:
datatype ficheiro = Text of string | Directoria of ficheiro list
Isto diz-nos que um ficheiro ou é definido pelo seu nome ou é uma directoria. Neste último caso pode ser composta por uma lista de (sub) ficheiros. As construtoras tem os tipos
Text: string ficheiroDirectoria: ficheiro list ficheiro
Tipos recursivos
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
19Universidade da Madeira
Departamento de Matemática e Engenharias
Assim, um exemplo de um valor do tipo ficheiro será:
val fich1 = Directoria [Text “f1”, Text “f2”, Directoria [Text “f3”, Directoria [ ]], Text “f4” ]
onde o valor fich1: ficheiro é uma directoria com uma lista de quatro sub-ficheiros. O terceiro sub-ficheiro é também uma directoria com dois sub-ficheiros, um dos quais é uma directoria vazia.
Tipos recursivos
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
20Universidade da Madeira
Departamento de Matemática e Engenharias
As novas definições de tipos até agora apresentadas têm todas a mesma forma genérica.
Um novo nome de um tipo é introduzido, juntamente com um número finito de funções construtoras, cada uma sendo ou uma constante ou tendo um argumento de um tipo especificado.O tipo dos argumentos pode envolver referências recursivas ao novo tipo.Em geral, podemos querer introduzir diversos novos tipos simultaneamente, onde os argumentos das construtoras podem ser de qualquer um dos novos tipos.
Tipos mutuamente recursivos
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
21Universidade da Madeira
Departamento de Matemática e Engenharias
Isto permite recursão mútua entre os tipos, como mostra o exemplo seguinte:
datatype part = Basicpart of string | Compoundpart of (string x components list)
and components = Quantity of (part x int)
Aqui as construtoras têm os tipos:
Basicpart: string partCompoundpart: (string x components list) partQuantity: (part x int) components
Tipos mutuamente recursivos
Elsa Carvalho
Programação em Lógica e Funcional (2000/01)(Actualizado em 2005/06)
22Universidade da Madeira
Departamento de Matemática e Engenharias
O tipo definido acima representa peças simples e compostas. Uma peça simples é da forma Basicpart “partA” ou Basicpart “partB” em que a string é o identificador da peça.
Uma peça composta é por exemplo “partC” em cuja composição entram 3 unidades da “partA” e 5 unidades da “partB”.
val partC = Compoundpart (“partC”, [Quantity (Basicpart “partA”, 3),
Quantity (Basicpart “partB”, 5)])
Tipos mutuamente recursivos