43
1 CAPITOL 4 TP Definició de nous tipus Es habitual en molts llenguatges de programació la possibilitat de donar noms a tipus construïts a partir del sistema habitual de tipus. Així, a SML podem fer type arc = string * string * int type parell = (int x int) list En molts llenguatges de programació aquesta és l’única opció. En llenguatges moderns és possible definir nous tipus, les seves constants i els constructors, sense necessitat de fer servir els tipus predefinits. Per tal de fer-ho necessitem una forma d’especificar el nom del tipus, definir-ne els valors i determinar els valors de una forma canònica per tal de mostrar-los.

Definició de nous tipus

  • Upload
    shelley

  • View
    43

  • Download
    0

Embed Size (px)

DESCRIPTION

Definició de nous tipus. Es habitual en molts llenguatges de programació la possibilitat de donar noms a tipus construïts a partir del sistema habitual de tipus. Així, a SML podem fer type arc = string * string * int type parell = (int x int) list - PowerPoint PPT Presentation

Citation preview

Page 1: Definició de nous tipus

1 CAPITOL 4CAPITOL 4

TPTP Definició de nous tipus

Es habitual en molts llenguatges de programació la possibilitat de donar noms a tipus construïts a partir del sistema habitual de tipus. Així, a SML podem fer

type arc = string * string * int

type parell = (int x int) list

En molts llenguatges de programació aquesta és l’única opció.

En llenguatges moderns és possible definir nous tipus, les seves constants i els constructors, sense necessitat de fer servir els tipus predefinits.

Per tal de fer-ho necessitem una forma d’especificar el nom del tipus, definir-ne els valors i determinar els valors de una forma canònica per tal de mostrar-los. Això ho podrem fer via el concepte de constructor.

Page 2: Definició de nous tipus

2 CAPITOL 4CAPITOL 4

TPTP Nous tipus

Els constructors que veurem seran o bé constants del nou tipus, o bé funcions que produiran com a resultat nous valors del mateix tipus. Les funcions serviran per a construir nous valors i també serviran de representació canònica dels valors.

El problema bàsic és definir els contructors. En termes de què? Altres llenguages (v.g. Pascal) permeten definir nous tipus (v.g. records) a partir de detalls de representació en memòria. Nosaltres simplement definirem el noms dels constructors i deixarem els detalls de representació als compiladors.

A més, no necessitarem definir funcions de selecció de valors ja que podrem definir funcions sobre ells via acarament de patrons.

Per tant, per tenir nous tipus la única cosa necessària és poder definir (1) el nom, (2) els noms dels constructors, i (3) els tipus dels arguments del constructor.

Page 3: Definició de nous tipus

3 CAPITOL 4CAPITOL 4

TPTP Tipus amb constants

Alguns tipus consisteixen en l’enumeració dels seus valors, com en Pascal.

datatype vents = Mestral | Garbi | Migjorn | Xaloc

datatype peixos = Rap | Neru | Llobarro | Dentol | Orada

datatype salses = Allioli | Romesco | Holandesa | Mahonesa

datatype coccio = Forn | Brasa | Fregit | Alvapor | Bullit

datatype acompanyament = Patates | Pesols | Pure

Ara els valors (constants) definits tenen el tipus al qual pertanyen.

Mestral:vents, Neru:peixos, Brasa:coccio, Romesco:salses

Els nous tipus es poden combinar amb tipus preexistents.

(Rap, Romesco, Forn, “esbo”):peixos*salses*coccio*string

Page 4: Definició de nous tipus

4 CAPITOL 4CAPITOL 4

TPTP Us dels nous tipus

Les constants introduïdes fins

ara (predefinides) són:

datatype bool = true | false

datatype unit = ()

Tots els tipus definits per enumeració són tipus amb igualtat. Per tant, es permeten comparacions com:

Mestral = Garbi, member [Llobarro, Neru] Llobarro = true

Tanmateix, les constants poden fer-se servir en patrons

fun combi (Rap, Romesco) = “bonissim”

| combi (Llobarro, x) = “bo”

| combi (Dentol, Mahonesa) = “regular”

| combi (x, y) = ““

Page 5: Definició de nous tipus

5 CAPITOL 4CAPITOL 4

TPTP Constructors amb arguments

Un constructor amb un argument és una funció que produeix un valor del nou tipus quan rep un argument del tipus adient. El tipus de l’argument del constructor es defineix quan es defineix el nom del nou tipus.

datatype plat = Plat of (peixos * coccio)

plat és el nom del nou tipus i Plat és el nom del constructor. El constructor té el tipus següent:

Plat : (peixos*coccio)->plat

i podem construir expressions com:

Plat(Llobarro, Alvapor)

Let val combi = (Dentol, Fregit) in Plat combi end

map Plat [(Orada,Alvapor),(Neru,Fregit),(Dentol,Bullit)]

Page 6: Definició de nous tipus

6 CAPITOL 4CAPITOL 4

TPTP Paper dual dels constructors

Les expressions anterior, un cop avaluades, donen com a resultats

Plat(Llobarro, Alvapor)

Plat(Dentol, Fregit)

[Plat(Orada, Alvapor), Plat(Neru, Fregit), Plat(Dentol, Bullit)]

En aquestes avaluacions es veu el paper dual dels constructors: com a funcions i com a components dels valors canònics.

En general si C: T -> T’ és un constructor i E és una expressió de tipus T que avalua en el valor a llavors C E avalua a la forma canònica C a.

Un valor de tipus (peixos*coccio), per exemple (Llobarro, Alvapor), no és un valor del tipus plat. Tots els valors de tipus plat han de tenir la forma Plat(x,y).

Page 7: Definició de nous tipus

7 CAPITOL 4CAPITOL 4

TPTP Ús dels constructors en acaraments

La importància del paper dual dels constructors es veu en les definicions per acarament, com les següents:

fun tall (Plat(peix, coccio)) = peix

fun company(Plat(peix, coccio)) = coccio

fun alabrasa (Plat(peix, Brasa)) = true

| alabrasa (Plat(peix, coccio)) = false

Es considera una bona pràctica de programació definir nous tipus; àdhuc en els casos en que existeix un tipus predefinit que ens podria servir. La raó és la millora en la llegibilitat i la protecció contra operacions no desitjades.

datatype alçada = Al of int

datatype pes = Pe of int

Al(180) + Pe(100) donarà error!

Page 8: Definició de nous tipus

8 CAPITOL 4CAPITOL 4

TPTP Tipus amb diversos constructrs

Així podem veure el tipus dels constructors:

Autor: string -> autor

Anonim: autor

Butxaca: (string * autor list) -> llibre

Pasta: (string * autor list) -> llibre

datatype autor = Autor of string

|Anonim

datatype llibre = Butxaca of (string * autor list)

|Pasta of (string * autor list)

fun titol(Butxaca(s, l)) = s

| titol(Pasta(s, l)) = s

fun autors(Butxaca(s, l)) = l

| autors(Pasta(s, l)) = l

Page 9: Definició de nous tipus

9 CAPITOL 4CAPITOL 4

TPTP Igualtat

Si els components d’un tipus són tipus amb igualtat també ho serà el tipus definit a partir d’ells.

(Pasta(a, b) = Pasta(c, d)) == (a = c) & (b = d)

(Pasta(a, b) = Butxaca(c, d)) == false

Podem entendre els tipus així definits com a unions disjuntes dels components arguments dels constructors. Així, si volguéssim definir llistes amb components heterogenis podriem fer-ho a traves de la definició d’un nou tipus que barregés diferents tipus amb constructors diferents.datatype mix = Int of int

| Str of string

val mix1 = [Int 5, Str “pepet”, Str “anna”, Int 6]

fun getint [] = []

| getint (Int n :: x) = n::getint x

| getint (Str s :: x) = getint x

Page 10: Definició de nous tipus

10 CAPITOL 4CAPITOL 4

TPTP Tipus recursius

Les definicions de tipus es consideren recursives per defecte, es a dir, el nom del tipus essent definit pot aparéixer a la part dreta de la definició.

datatype fitxer = Text of string list

| Directori of fitxer list

val fitxer1 =

Directori[Text s1, Text s2,

Directori[Text s3, Directory []],

Text s4]

Amb definicions de tipus recursives es poden definir conjunts infinits de valors.

datatype number = Zero | Succ of number

fun numplus (Zero, m) = m

| numplus (Succ n, m) = Succ(numplus (n, m))

Page 11: Definició de nous tipus

11 CAPITOL 4CAPITOL 4

TPTP Tipus mutuament recursius

Podem definir més d’un tipus alhora i definir-los de forma mútuament recursiva.

datatype part = Basicpart of int

| Compoundpart of (int * components list)

and components = Quantity of (part * int)

Els constructors tenen els tipus següents:

Basicpart: int -> part

Compoundpart: (int * components list) -> part

Quantity: (part * int) -> components

Així, tenim:val motor = Basicpart 32

val roda = Basicpart 35

val cotxe = Compoundparts (36, [Quantity(motor, 1), Quantity(roda, 4)]

Page 12: Definició de nous tipus

12 CAPITOL 4CAPITOL 4

TPTP Operadors de tipus

Els tipus bàsics (int, string, ...) es poden veure com a casos degenerats de constructors de tipus (sense arguments). Els operadors * i -> són paramètrics sobre arguments de tipus tipus.

Podem definir nous tipus paramètrics de la mateixa manera que fins ara, però precedint el nom del nou tipus per una n-pla de variables de tipus. El nou tipus així definit s’anomena tipus paramètric. Les variables de tipus es poden fer servir com a arguments dels constructors, fent que aquests siguin polimòrfics. Així, per exemple, podem generalitzar la definició de matrius d’enters:

datatype int_matrix = Matrix of (int list list)

definint:

datatype ‘a matrix = Matrix of (‘a list list)

Page 13: Definició de nous tipus

13 CAPITOL 4CAPITOL 4

TPTP Més tipus paramètrics

Ara, per a qualsevol tipus T, també tenim el tipus T matrix. Essent, per tant, el constructor Matrix del tipus polimòrfic següent:

Matrix: (‘a list list) -> ‘a matrix

Podem definir ara:Matrix [[1,2],[3,4][5,6],[7,8]]: int matrix

Matrix[[Matrix [[true,false],[false,false]]]]: (bool matrix) matrix

ara podem veure que l’operador de construcció de llistes no és necessari entendre’l com a un operador predefinit especial.

datatype ‘a list = :: of ‘a * ‘a list

| []

Tots els tipus paramètrics poden ésser definits d’aquesta manera excepte els operadors bàsics * i ->.

Page 14: Definició de nous tipus

14 CAPITOL 4CAPITOL 4

TPTP Example

El tractament del fracàs representa un problema en els llenguatges fortament tipats. Per exemple, en un problema de cerca, en cas de tenir èxit podem retornar un component de l’estructura, però en cas de fracàs hem de retornar algun valor especial que normalment no és del tipus dels components de l’estructura. Una solució més elegant és fer servir l’operador de tipus següent:

Una funció que cerqui un valor de tipus ‘a retornarà un valor de tipus ‘a possible. Ok(n) en cas d’èxit o Fail en cas de fracàs.

datatype ‘a possible = Ok of ‘a

| Fail

fun findpos [] = Fail

| findpos (n::x) = if n>0 then Ok(n) else findpos x

fun report Fail = “Fracas en trobar un positiu”

| report (Ok n) = “The value is:” ^ stringofint n

Page 15: Definició de nous tipus

15 CAPITOL 4CAPITOL 4

TPTP Arbres binaris

Els arbres i els grafs són unes de les estructures més comunes en informàtica. Començarem amb l’estudi dels arbres binaris.

Per definir l’operador de tipus per representar arbres binaris només ens hem de fixar en la forma en que aquests es poden construir.

1

92 4 7

1

92 4 7

/\

/\ /\

/\

LfLfLfLf

Lfdatatype ‘a bintree =

Lf of ‘a

| /\ of (‘a bintree * ‘a bintree)

val bt = Lf 1 /\ ((Lf 2 /\ Lf 4) /\ (Lf 7 /\ Lf 9))

Page 16: Definició de nous tipus

16 CAPITOL 4CAPITOL 4

TPTP Funcions sobre arbres binaris

fun left (t1 /\ t2) = t1

| left (Lf a) = error “left undefined”

fun right (t1 /\ t2) = t2

| right (Lf a) = error “right undefined”

fun isleaf (Lf a) = true

| isleaf (t1 /\ t2) = false

fun leftmostleaf (t1 /\ t2) = leftmostleaf t1

| leftmostleaf (Lf a) = a

fun sumoflf (t1 /\ t2) = sumoflf t1 + sumoflf t2

| sumoflf (Lf a) = a

;; I així tenim

leftmostleaf ((Lf 2 /\ Lf 4) /\ (Lf 7 /\ Lf 9)) ==

leftmostleaf (Lf 2 /\ Lf 4) ==

leftmostleaf Lf 2 == 2

Page 17: Definició de nous tipus

17 CAPITOL 4CAPITOL 4

TPTP Operacions d’ordre superiorMoltes operacions sobre arbres impliquen el seu recorregut i

l’aplicació de determinades funcions sobre les fulles, els resultats de les quals són combinats amb una altra funció. Utilitzarem programació d’ordre superior per fer-ho.

fun btreeop f g (Lf a) = f a

| btreeop f g (t1/\t2)= g(btreop f g t1)(btreeop f g t2)

Així operacions definides anteriorment es poden veure de forma simple amb aquesta funció:

Altres impliquen el manteniment de l’estructura de l’arbre binari però aplicant-hi una funció a les fulles. Vegeu:

val sumoflf = btreeop I plus

fun btreemap f = btreeop (Lf o f) join

and join t1 t2 = t1 /\ t2

btreemap (plus 1) (Lf 5 /\ (Lf 2 /\ Lf 8)) ==

(Lf 6 /\ (Lf 3 /\ Lf 9))

Page 18: Definició de nous tipus

18 CAPITOL 4CAPITOL 4

TPTP Arbres amb valors als nodes interns

El primer constructor és un cas degenerat d’arbres sense nodes. Per evitar haver d’escriure en excés introduïm:

datatype ‘a tree = Empty

| Tr of (‘a tree *’a * ‘a tree)

fun leaf a = Tr (Empty, a, Empty)

i així

val tree1 = leaf 3

val tree2 = Tr(leaf 2, 5, leaf 6)

Vegeu ara funcions sobre arbres:fun flatten Empty = []

| flatten (Tr(t1,a,t2)) = flatten t1@[a]@flatten t2

fun treemap f Empty = Empty

| treemap f (Tr(t1, a, t2)) =

Tr(treemap f t1, f a, treemap f t2)

Page 19: Definició de nous tipus

19 CAPITOL 4CAPITOL 4

TPTP Recorreguts d’arbres

Els arbres binaris tenen tres formes d’ésser recorreguts:

val inorder = flatten

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]

83 5

4 7

6inorder t = [3,4,5,6,7,8]

preorder t = [6,4,3,5,7,8]

postorder t = [3,5,4,8,7,6]

Page 20: Definició de nous tipus

20 CAPITOL 4CAPITOL 4

TPTP Ordre superior i ordenacións

Aquesta mena d’arbres s’utilitza habitualment per a mantenir l’ordre d’elements de manera més eficient que els algorismes sobre llistes. Un cop ordenats els elements es fa un aplanament per obtenir la llista ordenada.

fun treeinsert Empty m = leaf m

| treeinsert (Tr(t1, n, t2)) m =

if (lessthan n) m then Tr(treeinsert t1 m, n, t2)

else Tr(t1, n, treeinsert t2 m)

fun treesort x = flatten (accumulate treeinsert Empty x)

fun treeop c g Empty = c

| treeop c g (Tr(t1, a, t2)) =

g(treeop c g t1, a, treeop c g t2)

Page 21: Definició de nous tipus

21 CAPITOL 4CAPITOL 4

TPTP Arbres més generals

Els tipus ‘a bintree i ‘b tree són casos particulars de:datatype (‘a, ‘b) abtree =

Leaf of ‘a

| Node of ((‘a, ‘b) abtree * ‘b * (‘a, ‘b) abtree)

Són arbres que tenen valors de tipus ‘b als nodes i valors de tipus ‘a a les fulles. Quan ‘b és unit no hi ha informació als nodes. Així Leaf i Node juguen el paper de Lf i /\. Per tant ‘a bintree és isomòrfic amb (‘a, unit) abtree. Si és a les fulles que tenim unit llavors Node juga el paper de Tr i Leaf () juga el paper de Empty; així (unit, ‘b) abtree és isomòrfic amb ‘b tree.

Una altra abstracció és arbres amb un nombre arbitrari de fills:

datatype ‘a vtree = Vtree of ‘a * (‘a vtree) list

type ‘a forest = (‘a vtree) list

Page 22: Definició de nous tipus

22 CAPITOL 4CAPITOL 4

TPTP Grafs

Un graf dirigit es defineix com a un conjunt de nodes i fletxes (arcs) lligant parelles de nodes

Hi han diferents variants, grafs amb etiquetes als arcs, grafs no dirigits, multigrafs (amb més d’un arc entre dos nodes), acíclics, etc. El conjunt de nodes es considera finit habitualment, tot i que es poden considerar grafs infinits. Definirem un tipus particular de graf i algorismes de cerca a sobre d’ell.

Per tal de definir algorismes de cerca ens concentrarem en representar l’accessibilitat dels nodes veins a un node donat.

Page 23: Definició de nous tipus

23 CAPITOL 4CAPITOL 4

TPTP Tipus de dades graf

‘a és el tipus dels nodes del graf. Els grafs es construeixen a partir d’una funció successor que donat un element de tipus ‘a ens dóna la llista dels seus nodes veins.

Graf: (‘a -> ‘a list) -> ‘a graph

Per exemple, veiem la construcció de dos grafs a partir de dues funcions successor.

datatype ‘a graf = Graf of (‘a -> ‘a list)

fun succ1 b = [not b]

val graf1 = Graf (succ1)

fun succ2 n = if n < 2 or n > 20 then []

else (n-1)::succ2 (n div 2)

val graf2 = Graf (succ2)

Page 24: Definició de nous tipus

24 CAPITOL 4CAPITOL 4

TPTP Cerca

La cerca d’un graf es fa a partir d’un node. La funció successor ens donarà els successors del node per a continuar la cerca a partir d’ells. Guardant els nodes visitats en una llista evitarem repetir la cerca a partir de nodes ja visitats.

fun depthsearch (Graph succ) p startnode =

let fun find visited [] = Fail

| find visited (a::x) =

if member visited a then find visited x

else if p a then Ok a

else find (a::visited) (succ a @ x)

in find [] [startnode] end

Page 25: Definició de nous tipus

25 CAPITOL 4CAPITOL 4

TPTP Cerca II

Fixeu-vos que la única diferència entre els dos algorismes de cerca és la forma d’enmagatzemar els nodes que resten per visitar. En depthfirst els successors d’un node són visitats primer, en breadthfirst al final.

fun breadthsearch (Graph succ) p startnode =

let fun find visited [] = Fail

| find visited (a::x) =

if member visited a then find visited x

else if p a then Ok a

else find (a::visited) (x @ succ a)

in find [] [startnode] end

Page 26: Definició de nous tipus

26 CAPITOL 4CAPITOL 4

TPTP Associacions

Les associacions, igual que els grafs, són estructures de dades que contenen funcions com a components.

Un concepte natural en llenguatges de programació són els conjunts de vinculacions, també anomenats associacions. En realitat són funcions parcials. Per exemple, una llista de noms de persones i edats, ens permet, donat un nom, obtenir la seva edat; a més, podem definir operacions per a afegir noves parelles, canviar valors, etc. Una solució tradicional és considerar les associacions com a llistes sobre les que es cerquen valors:

datatype (‘a, ‘b) associations =

Assoc of (‘a * ‘b) list * (‘a, ‘b) associations

| Emptyassoc

fun assoc list oldassocs = Assoc(list, oldassocs)

Page 27: Definició de nous tipus

27 CAPITOL 4CAPITOL 4

TPTP Implementació funcional

Hi ha una manera més natural d’implementar les associacions, i és a través de tipus funcionals.type (‘a, ‘b) associations = ‘a -> ‘b

fun assoc [] oldassocs a = oldassocs a

| assoc ((a1,b1)::rest) oldassocs a =

if a = a1 then b1 else assoc rest oldassocs a

assoc: (‘‘a * ‘b) list -> (‘‘a -> ‘b) -> ‘‘a -> ‘b

fun emptyassoc a = error “cap assocoacio trobada”

val exemple1 = assoc [(1, “a”),(2,”b”)] emptyassoc

val exemple2 = assoc [(2,”dos”),(5,”pep”)] exemple1

;;o bé directament amb funcions

val atlocation = assoc [(1,10),(2,20)] I

val tostring = assoc [] chr

Aquests darrers exemples mostren la similitut entre les associacions i els arrays dels llenguatges imperatius.

Page 28: Definició de nous tipus

28 CAPITOL 4CAPITOL 4

TPTP Estructures de prioritats

Hi ha molts conceptes de programació (cues, piles, llistes de prioritat) que es poden unificar amb una estructura de dades basada en components funcionals.

1) Les piles afegeixen i eliminen elements de forma LIFO.

2) Les cues afegeixen i eliminen elements de forma FIFO.

3) Les llistes amb prioritat afegeixen i eliminen elements segons un ordre de prioritat.

Podriem fer tipus diferents per a cadascuna de les estructures. Ara bé, també es pot fer amb una estructura que permeti:

1) Afegir nous elements per a obtenir una nova estructura.

2) Obtenir un element a partir d’una estructura deixant una estructura del mateix tipus.

3) Calcular quants elements hi són representats.

Page 29: Definició de nous tipus

29 CAPITOL 4CAPITOL 4

TPTP Definició del tipus de dadesdatatype ‘a priority = Prio of

(‘a -> ‘a priority *

unit -> (‘a * ‘a priority) *

int)

Els dos components funcionals s’utilitzen per a afegir i eliminar components. El tercer component s’utilitza per a mantenir el nombre d’elements presents en l’estructura.

Ja que el tipus unit només té un valor, (), ens proporciona un argument buit per a la segona funció. La rao per no col.locar directament el resultat de la funció en comptes de la funció s’explica per la possibilitat de definir avaluació mandrosa en llenguatges estrictes (ho veurem més endavant).

Estem definint les funcions per afegir i eliminar components d’una estructura com a components locals de l’estructura en un estil orientat objecte.

Page 30: Definició de nous tipus

30 CAPITOL 4CAPITOL 4

TPTP Piles

Abans de definir valors del tipus de dades definit necessitem definir la representació interna a partir de la qual definir els valors.

fun additem (Prio (f, g, n)) a = f a

fun getitem (Prio (f, g, n)) x = g ()

fun sizestruct (Prio (f, g, n)) = n

;; amb tipus

additem: ‘a priority -> ‘a -> ‘a priority

getitem: ‘a priority -> (‘a * ‘a priority)

sizestruct: ‘a priority -> int

fun mkstack (list, n) =

let fun f a = mkstack (a::list, n+1)

fun g () = if n < 1 then error “underflow”

else (hd list, mkstack (tl list, n-1)

in Prio(f, g, n) end

val pilabuida = mkstack ([], 0)

Page 31: Definició de nous tipus

31 CAPITOL 4CAPITOL 4

TPTP Encapsulament

Ens interessa que l’únic ús de la funció mkstack es faci a partir de la pila buida. Així, encapsulem:

local fun mkstack (list, n) =

let fun f a = mkstack (a::list, n+1)

fun g () = if n < 1 then error “underflow”

else (hd list, mkstack (tl list, n-1)

in Prio(f, g, n) end

in

val emptystack = mkstack ([], 0)

end

Ara podem definir piles a partir de la pila buida i les operacions additem i getitem.

Page 32: Definició de nous tipus

32 CAPITOL 4CAPITOL 4

TPTP Cues

Le cues les definim d’una manera un pel diferent, malgrat que tornem a fer servir un enter i una llista com a representacions internes. La diferència rau en el fet que l’enter no representa la longitut de la llista sinó que és una fita inferior del nombre d’elements.

local fun mkqueue (list, n) =

let fun f a = mkqueue (a::list, n+1)

fun g () = if n < 1 then error “underflow”

else (select n list, mkqueue (list, n-1)

in Prio(f, g, n) end

in

val emptyqueue = mkqueue ([], 0)

end

Els elements són afegits pel capdevant de la llista però no són eliminats. El valor n ens dóna la posició de l’element a treure.

Page 33: Definició de nous tipus

33 CAPITOL 4CAPITOL 4

TPTP PrioritatsSi suposem una funció before:‘a -> ‘a -> bool que ens dóna un ordre

total sobre els elements de ‘a. fun emptyprio (before) =

let fun mkprio (list, n) =

let fun f a = mkprio (insertwrt before a list, n+1)

fun g () = if n < 1 then error “underflow”

else (hd list, mkprio (tl list, n-1)

in Prio(f, g, n) end

in

mkprio ([], 0)

end

fun insertwrt before a [] = [a]

| insertwrt before a (b::x) = if (before b) a

then a::b::x

else b::(insertwrt before a x)

Si before b a és sempre cert tenim piles; si és sempre fals tenim cues.

Page 34: Definició de nous tipus

34 CAPITOL 4CAPITOL 4

TPTP Problemes i solució

Les definicions locals poden ésser redefinides a nivell superior i per tant poden donar lloc a resultats erronis. Això pot ser convenient si són resultats volguts, però poden donar problemes si no és així.

Quan definirem els tipus abstractes veurem que podem definir totes les funcions additem getitem, emptystack, etc, en el mateix moment en que definim els tipus.

Localitzarem totes les definicions i evitarem que el programador pugui introduir, per exemple, estructures de prioritats amb representacions internes diferents.

Page 35: Definició de nous tipus

35 CAPITOL 4CAPITOL 4

TPTP Algebres lliures

La definició matemàtica d’àlgebra és la d’un conjunt de valors junt amb un conjunt de funcions que operen a sobre d’ells.

La construcció de l’algebra lliure és una manera de construir l’àlgebra a partir d’un conjunt de constructors i la informació de llurs tipus. Ho veurem amb un exemple.

datatype t = C0

| C1 of t

| C2 of t * t

t és el tipus definit, C0, C1 i C2 són els constructors. Si classifiquem els termes segons la complexitat (profunditat), tenim a primer nivell (profunditat 1): C0. A segon nivell: C1(C0), C2(C0, C0). A profunditat tres tenim:C1(C1(C0)), C1(C2(C0,C0)), ...

L’àlgebra es diu generada lliurement a partir del conjunt de valors i dels constructors.

Page 36: Definició de nous tipus

36 CAPITOL 4CAPITOL 4

TPTP Inducció estructural

Donada una definició d’un nou tipus t que introdueix els constructors C0, ..., Cn tenim el següent:

Principi d’inducció estructural per al tipus t

Per tal de provar que P(x) es satisfà per tots els valors x:t.

és suficient provar que per a qualsevol constructor Ci:

(1) si Ci és una constant llavors P(Ci) és cert;

(2) si Ci és un constructor amb un argument de tipus:

T1 x T2 x ... x Tj (j >0)

llavors P(Ci(x1, ..., xj)) se segueix de les assumpcions P(y1), ..., P(yk), on y1, ..., yk són els membres de x1, ..., xj de tipus t.

Assumim que Tk (1 <= k <= j) es o bé t o no involucra t.

(És un cas particular de la inducció completa prenent l’ordre ben fundat

de les subexpressions.)

Page 37: Definició de nous tipus

37 CAPITOL 4CAPITOL 4

TPTP Exemple: Arbres binaris

Per tal de fer una prova per inducció estructural de P(x) per a tot x:T bintree, hem de provar:

(1) P(Lf(a)) es verifica per a tot a: T,

(2) P(t1 /\ t2) es verifica assumint P(t1) i P(t2).

Considerem, per exemple, la funció

datatype ‘a bintree =

Lf of ‘a

| /\ of (‘a bintree * ‘a bintree)

fun reflect (Lf a) = Lf a

| reflect (t1 /\ t2) = (reflect t2) /\ (reflect t1)

Volem provar reflect(reflect x) = x

Page 38: Definició de nous tipus

38 CAPITOL 4CAPITOL 4

TPTP Prova

Cas base:

reflect(reflect(Lf a)) = reflect(Lf a) = Lf a

Cas inducció:

reflect(reflect t1) = t1

reflect(reflect t2) = t2

és fàcil veure

reflect(reflect(t1 /\ t2)) = (def reflect)

reflect((reflect t2) /\ (reflect t1)) = (def reflect)

(reflect(reflect t1)) /\ (reflect(reflect t2)) = (HI)

t1 /\ t2

Page 39: Definició de nous tipus

39 CAPITOL 4CAPITOL 4

TPTP Exemple: Arbres

Per tal de fer una prova per inducció estructural P(x) per a tot x:T tree, hem de provar:

(1) P(Empty) es verifica,

(2) P(Tr(t1, a, t2)) es segueix de P(t1) i P(t2) per a tot a:T i t1, t2 : ‘a tree.

Considerem, per exemple, la funció

datatype ‘a tree = Empty

| Tr of (‘a tree *’a * ‘a tree)

fun orderedtree (Empty) = true

| orderedtree (Tr(t1,a,t2)) =

alltree (lessthan a) t1 & alltree (greatereq a) t2 &

orderedtree t1 & orderedtree t2

and alltree p (Empty) = true

| alltree p (Tr(t1,b,t2)) =

alltree p t1 & p b & alltree p t2

and greatereq a b = not (lessthan a b)

Page 40: Definició de nous tipus

40 CAPITOL 4CAPITOL 4

TPTP

Recordem la funció d’inserció ordenada en arbres:fun treeinsert Empty m = leaf m

| treeinsert (Tr(t1, n, t2)) m =

if (lessthan n) m then Tr(treeinsert t1 m, n, t2)

else Tr(t1, n, treeinsert t2 m)

Provarem que per a tot t:int tree and n:intif orderedtree t == true then orderedtree (treeinsert t n) == true

Cas base:

orderedtree(treinsert Empty n) == orderedtree(Tr(Empty, n, Empty)) == true

Pas d’inducció: Suposem

(1) orderedtree(t1) == true implies orderedtree(treeinsert t1 n) == true

(2) orderedtree(t2) == true implies orderedtree(treeinsert t2 n) == true

Page 41: Definició de nous tipus

41 CAPITOL 4CAPITOL 4

TPTP Prova

hem de provar que de (3) orderedtree(Tr(t1, a, t2)) == true

es pot obtenir (4) orderedtree (treeinsert (Tr(t1, a, t2)) n) == true

Suposarem (3) i analitzarem dos casos

Cas n < a: (4) es simplifica a orderedtre(Tr(treeinsert t1 n, a, t2)) ==true

això és equivalent a la conjunció de les sentències següents: (5) orderedtree(treeinsert t1 n) == true

(6) orderedtree(t2) == true

(7) alltree (greatereq a) t2

(8) alltree (lessthan a) (treeinsert t1 n)

(5, 6, 7) segueixen de (3) i (1). (8) segueix del lema següent:alltree p t == true and p n == true implies alltree p (treeinsert t n) == true

Cas n>= a: anàleg a partir de (2) en comptes de (1) i del lema previ.

Page 42: Definició de nous tipus

42 CAPITOL 4CAPITOL 4

TPTP Forma general de definició de tipus

Topi són els nous operadors de tipus. TVarsi són sequències de variables de tipus. Tij és una expressió de tipus. Cij és un nou constructor de tipus Tij ->(TVarsi Topi). Qualsevol de les expressions of Tij pot ser suprimida. Tots els constructors en la definició han de ser diferents. Les expressions de tipus poden utilitzar tipus definits prèviament però també referències recursives a Top1...Topm i també a les variables de la seva part esquerra TVarsi . Per tant les variables poden repetir-se ja que el seu abast és només la part dreta.

datatype TVars1 Top1 = C11 of T11|C12 of T12 ...|C1n1 of T1n1

and TVars2 Top2 = C21 of T21|C22 of T22 ...|C2n2 of T2n2

...

and TVarsm Topm = Cm1 of Tm1|Cm2 of Tm2 ...|Cmnm of Tmnm

datatype ‘a point = Point of ‘a

and ‘a line = Line of (‘a point * ‘a point)

;;és equivalent a

datatype ‘a point = Point of ‘a

and ‘b line = Line of (‘b point * ‘b point)

Page 43: Definició de nous tipus

43 CAPITOL 4CAPITOL 4

TPTP Localitat i errors

Aquesta expressió donarà error ja que la redefinició de bool crea un nou tipus i fa que l’operació & no estigui definida per aquest nou tipus. Aquesta forma de definició de nous tipus es diu generativa.

Problemes similars apareixen quan recarreguem un fitxer que conté definicions de tipus. Al recarregar, objectes creats anteriorment (funcions) i utilitzats en el nou contexte poden donar errors de tipus.

let datatype bool = true | false

in true & false end

let datatype T1 = Localval1 | Localval2

in Localval2 end

El resultat d’aquesta expressió seria Localval2:T1, però que vol dir això en el contexte extern? El resultat hauria de ser un error. Els modules de SML proporcionaran un mecanisme per controlar l’abast de la definició de tipus.