Abstrakta datatyper Moduler nr 12 Signaturer, strukturer och funktorer
Abstrakta datatyper Konkreta typer används som representationer Operationer på representationen kapslas in Operationer och datastrukturer samlas på ett ställe Underliggande representationen göms Endast viktiga operationer och egenskaper visas utåt Fördelar: Program som använder datatypen blir oberoende av en specifik representation av datatypen. Det blir lättare att effektivisera och förändra ett dataobjekt internt.
Syntax: abstype typename = constructor [of type ] | constructor [of type ] ..… with declarations (val, fun, datatype,......) end De funktioner som definieras kallas ofta primitiva funktioner för datatypen. Endast de deklarerade funktionerna och värdena är tillsammans med typen synliga utåt. Konstruerarna är endast lokala.
Exempel naturliga tal abstype nat = Nat of int with exception MakeNat fun makeNat n = if n < 0 then raise MakeNat else Nat n fun showNat (Nat n) = n local type relop = int * int -> bool type intop = int * int -> int fun relOp (ff: relop) (Nat x) (Nat y) = ff (x,y) fun intOp (ff: intop) (Nat x) (Nat y) = makeNat (ff (x,y)) in val natEqual = relOp (op=) val natPlus = intOp (op+) val natMinus = intOp (op-) end
Resultat type nat exception MakeNat val makeNat = fn : int -> nat val showNat = fn : nat -> int val natEqual = fn : nat -> nat -> bool val natPlus = fn : nat -> nat -> nat val natMinus = fn : nat -> nat -> nat
Test Måste ha egna funktioner för att visa objekt och för att jämföra. val q1 = makeNat 3; > val q1 = - : nat val q2 = makeNat 23; > val q2 = - : nat natPlus q1 q2; > val it = - : nat Måste ha egna funktioner för att visa objekt och för att jämföra. showNat (natPlus q1 q2); > val it = 26 : int natEqual q1 q2; > val it = false : bool
Exempel Två abstype-värden kan ej jämföras Abstype-konstruerare kan ej användas ”utanför” definitionen q1 = q2; ! Toplevel input: ! q1 = q2; ! ^^ ! Type clash: expression of type ! nat ! cannot have equality type ''a fun nateq (Nat x) (Nat y) = x=y; … ! A constructor name expected
Rationella tal abstype rational = rat of int * int with val zero = rat (0,1) val one = rat (1,1) fun // (m,n) = if n > 0 then rat (m,n) else raise Error fun ++ (rat(m1,n1),rat(m2,n2)) = rat (m1*n2 + m2*n1,n1*n2) fun == (rat(m1,n1),rat(m2,n2)) = m1*n2 = m2*n1 end type rational val zero = - : rational val one = - : rational val // = fn : int * int -> rational val ++ = fn : rational * rational -> rational val == = fn : rational * rational -> bool
Ordnade träd som abstrakt datatyp Genom att baka in ordningsfunktionen i (den interna) representationen, så slipper man skicka den som explicit parameter till alla trädfunktioner. Det ordnade trädet representeras som par av en ordningsfunktion och ett binärt träd. Endast vid skapandet av ett träd måste ordningsfunktionen ges.
Exempel abstype 'a ordtree = ord of ('a order * 'a tree) and 'a tree = empty | node of 'a * 'a tree * 'a tree with fun make (less : 'a order) = ord (less,empty) fun add v (ord (less,t)) = let fun insert empty = node (v,empty,empty) | insert (node(nv,l,r)) = if less v nv then node (nv, insert l,r) else node (nv,l, insert r) in ord (less,insert t) end end; > type 'a ordtree > type 'a tree > val make = fn : 'a order -> 'a ordtree > val add = fn : 'a -> 'a ordtree -> 'a ordtree
Moduler i ML Structure paket med deklarationer Signature paket med typning av deklarationer Functor parametriserad struktur
Structure En structure är en samling deklarationer Funktionsbibliotek Mönster för en structure structure struct_name = struct any legal definition or other structure end
Paket med listfunktioner structure List_struct = struct exception Drop and Take fun take n alist = ........ fun drop n alist = ...... fun dropwhile pred alist =..... fun takewhile pred alist = ....... fun filter pred alist =..... end
Åtkomst av objekt i en struktur: Punktnotation structname.defname List_struct.take 3 [9,2,4,7,1]; val it = [9,2,4] : int list Explicit öppning open List_struct val first3 = take 3 Risk för namnkonflikter om flera strukturer öppnas. Kan ej stängas explicit.
Egenskaper Strukturer kan nästas En struktur kan öppnas inuti en annan Namn: List_struct.substruct.id En struktur kan öppnas inuti en annan En strukturdef. kan bindas till en annan struktur; structure L2_struct = List_struct Strukturer kan ej jämföras
- open List; > type 'a list = 'a General.list val foldr = fn : ('a * 'b -> 'b) -> 'b -> 'a General.list -> 'b val @ = fn : 'a General.list * 'a General.list -> 'a General.list val foldl = fn : ('a * 'b -> 'b) -> 'b -> 'a General.list -> 'b val find = fn : ('a -> bool) -> 'a General.list -> 'a option val concat = fn : 'a General.list General.list -> 'a General.list val nth = fn : 'a General.list * int -> 'a val drop = fn : 'a General.list * int -> 'a General.list val filter = fn : ('a -> bool) -> 'a General.list -> 'a General.list val length = fn : 'a General.list -> int val null = fn : 'a General.list -> bool val hd = fn : 'a General.list -> 'a val revAppend = fn : 'a General.list * 'a General.list -> 'a General.list val all = fn : ('a -> bool) -> 'a General.list -> bool val last = fn : 'a General.list -> 'a val take = fn : 'a General.list * int -> 'a General.list val map = fn : ('a -> 'b) -> 'a General.list -> 'b General.list
Signaturer En samling typning av deklarationer Ger typerna hos deklarationerna i en struktur. Skiljer på specifikation och implementation signature sig_name = sig exception E type T eqtype T datatype T val I : T structure S : Sig end
Exempel signature OBJ_sig = sig type OBJECT val grow : OBJECT -> OBJECT val shrink : OBJECT -> OBJECT end
Att notera type OBJ val namn : typ kan deklareras i en struktur antingen som en type eller en datatype val namn : typ används oavsett om typ är en funktionstyp En struktur kan associeras med en signatur. (En delmängd av) strukturens objekt måste matcha signaturens typ(er).
Exempel MLs svar: structure INT_struct : OBJ_sig = struct type OBJECT = int fun grow n = n+1 fun shrink n = n-1 end; MLs svar: structure INT_struct : OBJ_sig
Matchning struktur Û signatur 1) Varje deklaration i signaturen måste ha en motsvarande deklaration i strukturen. 2) Varje deklaration i strukturen som har en motsvarande typdeklaration i signaturen måste matcha denna typ. 3) Om en eqtype deklareras i signaturen måste motsvarande typ i strukturen ha likhet definierad. 4) En definition i en struktur som inte matchas av en deklaration i signaturen är inte åtkomlig utifrån.
shrink saknas structure Bad_struct1 : OBJ_sig = struct type OBJECT = string fun grow c = if (c>="a") … end Error: unmatched val spec: shrink
Felaktiga typer structure Bad_struct2 : OBJ_sig = struct type OBJECT = real fun grow n = floor (n+1.0) fun shrink n = floor (n-1.0) end; Error: value type in structure doesn't match signature spec name: grow spec: OBJECT -> OBJECT actual: real -> int
Dold deklaration structure CHAR2_struct : OBJ_sig = struct type OBJECT = string fun last l = l fun grow c = c fun shrink c = c end; > structure CHAR2_struct : OBJ_sig CHAR2_struct.last; > Error: unbound variable or constructor: last in path CHAR2_struct.last
signature NUM_sig = sig type object val Int2OBJ : int -> object val Real2OBJ : real -> object … end structure NUM_struct = struct datatype object = Int of int | Real of real val Int2OBJ = Int val Real2OBJ = Real structure USEFUL_struct:NUM_sig = NUM_struct structure USELESS_struct:OBJ_sig = NUM_struct val newOBJ = USELESS_struct.Int2OBJ 3; > Error: Unbound name.... val newOBJ = USEFUL_struct.Int2OBJ 3; > val newOBJ = Int 3: NUM_struct.object
Signaturer... Med hjälp av olika signaturer kan man få olika ”views” av samma struktur. En type i en signatur kan definieras som en datatype i en matchande struktur. Datatypens konstruerare blir då ”gömda” i strukturen. För att kunna använda den måste vi definiera primitiva funktioner som skapar objekt av datatypen och tar fram dess komponenter.
Signaturer Kan ej jämföras (ingen likhetsop.) Kan endast definieras på toppnivå, kan ej nästas. M.h.a nyckelordet include kan deklarationerna i en signatur inkluderas i en annan. Kan definieras samtidigt som en struktur structure S : sig ..... end = struct ............end Två strukturer kan dela en typ eller understruktur
Funktorer En avbildning från struktur till struktur med en struktur : signatur som parameter (en parametriserad struktur). En funktor appliceras på en aktuell struktur. Mönster för en functor functor funct_name(par_struct : par_sig): funct_sig = struct any legal deklarations or other structure using names declared in par_struct end
Funktorer Modularisering. Flexibilitet vad gäller vilken struktur som används i en funktor Dokumentation. Måste specificera typer Tydligt interface mellan bibliotek (param_struct) och program. Underlättar separat kodutveckling Möjliggör separatkompilering (med typkontroller)
Funktorer Funktorer kan ha en tom parameterlista, eller flera strukturer som parametrar Funktorer är ej funktioner (över strukturer); – kan ej skickas som parametrar – ej partiellt applicerbara