Abstrakt datatyp, ADT En ADT är en domän (en "mängd" av data) och en mängd av operationer (funktioner) som kan utföras på detta data. Själva beteckningen "abstrakt" kommer från att vid användningen av ADT:n behöver man inte veta något om implementationen. Implementationen kan eventuellt ändras senare och de ställen i koden som använder ADT:n kommer att fungera som tidigare.
ADT I C finns bastyper, t.ex. int, char, float Vi behöver inte bekymra oss om hur t.ex. en float lagras eller hur det går till att göra + mellan två float-värden. Även bastyperna kan alltså ses som ADT:er Vi är dock f.a. intresserade av de mer avancerade typer som vi själva skapar
Konkretisering av ADT i C-kod För denna kurs gäller: ADT:ns interface specificeras genom att man i en H-fil: Deklarerar en datatyp (vars namn representerar domänen). Deklarerar ett antal funktioner som utför något med en pekare till en variabel av denna typ. ADT:ns implementation görs i en C-fil Datatypen definieras (structen definieras) Funktionerna definieras (dvs koden ska skrivas)
Typiska ADT-funktioner Funktioner för att modifiera datat Funktioner för att fråga datat om något I denna kurs kommer det också att ingå i interfacet: En konstruktor: en funktion som inte har något annat syfte än att skapa ett data En destruktor: en funktion som bara syftar till att fria minne relaterat till datat
Fokus ADT är ett koncept som det tar ett tag att smälta. Det viktiga för er i denna kurs är: Håll isär vad som ska finnas i interfacet (h-fil) och implementation (c-fil) Håll isär ADT-definitionen från användningen av ADT:n. Syftet är att få en strukturerad problemlösningsmetod som mappar till en strukturerad implementation.
Mer gripbart... I boken och på lektioner tar vi upp stack, kö, lista, träd. Detta generella och återanvändbara ADT:er. Men ADT konceptet är inte begränsat till sådana fall Man kan kan i princip betrakta vilken domän som helst som en ADT. T.ex. Ett schackbräde i en schackspel Fiendeskepp i ett rymdfight-spel Person i ett personregister
ADT För varje funktion i en ADT anges Pre-condition: vilka krav på datat finns innan anropet. Post-condition: vad gäller för datat efter anropet. Vad som gäller för ett eventuellt returvärde.
ADT I C är precondition och postcondition kommentarer. Kan företrädesvis anges explicit som t.ex. /* Precondition: x is not 0 */ En mer rutinerad kan skriva en löpande text som säkert inkluderar innehållet i pre- och postcondition: /* The parameter x must not be 0 */
Typen Clock Säg att vi vill ha variabler som är av typen klocka. Värdemängden för en klocka är alla möjliga klockslag. Vad vill vi kunna göra med en klocka (vilka operationer)? Ställa klockan Avläsa klockan Ticka klockan Fråga om tiden
ADT:en konrektiserad i C-kod: Clock clock.h innehåller: ty pe de f s tru ct Cl oc k Clo ck; Cl oc k *Cl ock Cr eat e( in t h our s, in t m ins, in t sec s); vo id C loc kde st roy (C lo ck *cl oc k); in t Cl ock Set (C loc k *c loc k, in t h ou rs, int mi ns, i nt se cs) ; in t Cl ock Hou r( Clo ck * clo ck) ; in t Cl ock Min ut e(c lo ck *c loc k) ; in t Cl ock Sec on d(c lo ck *c loc k) ; vo id C loc kti ck (Cl oc k *cl ock ); vo id C loc kp rin t(c lo ck *c loc k) ;
ADT:en Clock /* Required: hours<24, mins<60, secs<60. Returns a pointer to a new clock set to a time as specified by the parameters, or NULL if memory allocation fails or the time is incorrect. */ Cl oc k * Clo ck Cr eat e( int ho ur s, int mi ns, int s ecs ); /* Free all memory related to `clock'. After the call `clock' is no longer a valid pointer. */ vo id Cl ock De st roy (C loc k * cl oc k);
ADT:en Clock /* Required: hours<24, mins<60, secs<60. After the call the clock pointed to by `clock' is set to the time as specified by the parameters. Returns 1 if successful, else 0. */ in t Clo cks et (C loc k *cl ock, in t h ou rs, in t mi ns, in t sec s) ; /* Returns the hour of the current time of the Clock pointed to by `clock'. */ in t Clo ckh ou r( Clo ck *c loc k) ; in t Clo ckm in ut e(c lo ck *cl oc k) ; in t Clo cks ec on d(c lo ck *cl oc k) ;
ADT:en Clock /* Postcondition: the time of `clock' is one second in the future compared to before the call. If the time before the call is 23:59:59 it will be 0:0:0 */ vo id Cl ock Ti ck (Cl oc k * clo ck ); /* Prints the current time of the Clock pointed to by `clock'. */ vo id Cl ock Pr in t(c lo ck *cl oc k) ;
Implementation För att göra verklighet av klockan måste vi ha en implementation Hur ska vi representera klockan?
ADT:en Clock i C-kod clock.c innehåller: #i nc lu de "cl oc k.h st ru ct Cl ock { in t h; /* Ho ur s */ in t m; /* Mi nu te s * / in t s; /* Se co nd s * / ; (f or ts... )
Implementation av funktionerna int ClockSet(Clock *clock, int hours, int mins, int secs){ int ok; if(iscorrecttime( hours,mins,secs)){ clock >h = hours; clock >m = mins; clock >s = secs; ok = 1; /* Success! */ el se { ok = 0; /* Wrong arguments */ return ok;
Skapa... Cl oc k *Cl ock Cr eat e( in t h our s, in t m ins,i nt se cs ){ Cl oc k * clo ck = NU LL ; if (i sco rre ct Ti me( ho urs,mi ns,s ecs )) { cl ock = ma ll oc( si zeo f(c lo ck )); if (c loc k) { Clo cks et (c loc k, hou rs, mi ns,se cs ); re tu rn clo ck ;
... och göra sig av med vo id C loc kde st roy (Cl oc k *cl ock ) { fr ee( clo ck );
Mer implementationer: in t Clo ckh ou r( Clo ck *c loc k) { re tu rn cl ock > h; in t Clo ckm in ut e(c lo ck *cl oc k) { re tu rn cl ock > m; in t Clo cks ec on d(c lo ck *cl oc k) { re tu rn cl ock > s; vo id Cl ock Print (C lo ck *cl oc k){ pr in tf( "% 02d :% 02d :% 02d ", clo ck >h, cl oc k > m, clo ck >s );
Mer implementationer: vo id Cl oc kti ck (Cl oc k * cl ock ){ cl oc k > s+ +; /* Let 1 sec of time elapse */ if (c lo ck >s == 60 ){ /* Minute border */ clo ck >s = 0; clo ck >m ++ ; if( clo ck > m = = 6 0) { /* Hour border */ clo ck >m = 0; clo ck >h ++ ; if( clo ck > h = = 24) { cl oc k > h = 0 ;
Att använda ADTen Clock main.c innehåller: in t mai n( voi d) { Cloc k *cl oc k; cloc k = C lo ckc rea te (9,15,0) ; Cloc kt ick (c loc k); Cloc kt ick (c loc k); Cloc kt ick (c loc k); Cloc kp rin t(clo ck ); Cloc kd est ro y(c loc k) ; retu rn 0; Programmet skriver ut 09:15:03
Hjälpfunktioner Funktioner som används internt av ADT:ns funktioner Deklareras static Finns inte med i h-filen. st at ic in t i sc orr ec tt ime (in t hou rs, in t mi ns, i nt sec s) { re tu rn ho ur s<2 4 & & mi ns< 60 && se cs <6 0;
Övning! Från Cloc kc re ate :... if (i sco rre ct Ti me( ho urs,mi ns,s ecs )) { c = m all oc (s ize of (Cl ock )) ; if (c ) { Clo cks et (c, h ou rs, min s, se cs) ;... Cl ock Se t anropar is Co rr ect Tim e så att den blir onödigtvis anropad dubbelt. Gör en alternativ implementation av Cl ock Cre at e så att det undviks! Interfacet får inte påverkas. (hela koden repeteras på nästa slide)
/* Required: hours<24, mins<60, secs<60. Returns a pointer to a new clock set to a time as specified by the parameters, or NULL if memory allocation fails or the time is incorrect. */ Cl oc k * Cl ock Cr eat e( int h our s, int m ins,i nt se cs) { Cl ock * c = N UL L; if (is Co rre ct Ti me( hou rs,mi ns,s ecs )){ c = m all oc (s ize of( Cl ock )) ; if (c ) Clo cks et (c, h our s, min s, se cs) ; re tur n c;
ADT:en Stack Ett sätt att strukturera data Tänk på en stack som en hög tallrikar: Du kan bara plocka översta, och bara lägga en tallrik överst
ADT:en Stack Lägga data överst på stacken kallas Push Plocka bort datat överst på stacken kallas Pop Titta på översta datat kallas Peek Man kan fråga hur många element det finns på en stack med StackSize Kan en stack bli full? Teoretiskt, nej, men det beror på implementationen. En stack kallas ibland för en LIFO kö
Stack, exempel Push( A ) Push( B ) A A
Stack, exempel Pop() Push( C ) A A A
Pre- och Postconditions för stacken Vad är pre-conditions för Push(stack, x)? Post-conditions? Push(stack, x) => stack är samma stack som förut men med elementet x överst Pre-conditions för Pop(stack)? Stacken är ej tom Post-conditions? Pop(stack) => stack Är samma stack som förut men med översta elementet bortaget Pre-conditions för Peek(stack)? Stacken är ej tom Post-conditions för Peek(stack)? Stacken är oförändrad
Konkretisering av interface i C typedef struct Stack Stack; Stack *StackCreate(void); void StackDestroy(Stack *stack); int Push(Stack *stack, void *x); void *Pop(Stack *stack); void *Peek(Stack *stack); int StackSize(Stack *stack);
/* Returns a pointer to a new empty Stack */ Stack *StackCreate(void); /* Frees all memory related to `stack'. After the call `stack' is no longer a valid pointer. */ void StackDestroy(Stack *stack); /* Pushes `x' onto the top of `stack'. After the call StackSize has increased by 1. Retunrs 1 on succes or 0 if it failed to push `x' onto `stack'.*/ int Push(Stack *stack, void *x); /* Pop the top element from `stack' and return it. After the call StackSize has decreased by 1 */ void *Pop(Stack *stack); /* Returns the top element of `stack'. */ void *Peek(Stack *stack); /* Returns the number of elements of `stack'. */ int StackSize(Stack *stack);
Konkretisering av implementation i C struct Stack { /* Array of user data pointers */ void **elems; /* Number of elements on the stack */ int nr_elem; ; Stack *StackCreate(void) { return calloc(1, sizeof(stack));
Konkretisering av implementation i C int Push(Stack *stack, void *x) { void **orig = stack >elems; int newsize = sizeof(void*)*(stack >nr_elem+1); int ok; stack >elems = realloc(stack >elems,newsize); if (stack >elems) { stack >elems[stack >nr_elem++] = x; ok = 1; else { stack >elems = orig; ok = 0; return ok;
Övning void *Pop(Stack *stack); void *Peek(Stack *stack); int StackSize(Stack *stack); void StackDestroy(Stack *stack);
Förslag till lösning Stackens minne kan också allokeras om. Notera att det inte blir minnesspill om vi inte allokerar om, med elems behåller vi en baspekare till hela minnet som friställs av StackDestroy void *Pop(Stack *stack) { return stack >elems[ stack >nr_elem];
Förslag till lösning void *Peek(Stack *stack) { return stack >elems[stack >nr_elem 1]; int StackSize(Stack *stack){ return stack >nr_elem; Observera att ordningen mellan de två free är avgörande. Allt annat än denna ordning är fel. Varför? void StackDestroy(Stack *stack){ free(stack >elems); free(stack);