elsa carvalho 72 universidade da madeira departamento de matemática e engenharias programação em...

22
Elsa Carvalho Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06) 1 Universidade da Madeira tamento 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

Upload: internet

Post on 22-Apr-2015

103 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 2: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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.

Page 3: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 4: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 5: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 6: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 7: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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.

Page 8: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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.

Page 9: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 10: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 11: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 12: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 13: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 14: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 15: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 16: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 17: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 18: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 19: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 20: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 21: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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

Page 22: Elsa Carvalho 72 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)

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