Exempel Antag att vi vill ha en generell stack. En stack är en mekanism som man kan lagra i och hämta från enligt principen sist in, först ut (eng LIFO). Man skall alltså kunna Skapa en stack Lägga värden av ospecicerad typ på stacken (push) Ta bort senast lagrade värde (översta) värdet från stacken (pop Ta reda på om stacken är tom Titta på senast lagrade (ej borttagna) värde på stacken Vi vill inte heller som användare av stackmekanismen behöva bry oss om hur den är implementerad. Vill kunna ha en typ Stack som vi använder i deklarationer. (2008-09-15 4.1 )
Hur skall vi representera stacken? Kravet på generell typ => ospecicerad pekare (void *) Array eller lista? Array enkel men x storlek Hur skall stacken representeras utåt? En stackpost, en pekare till en särskild stackpost eller räcker det med en pekare till första elementet? (2008-09-15 4.2 )
Vi försöker med enklast möjliga representation: en pekare till första elementet i en länkad lista. Det innebär att vi måste låta funktioner som ändrar i stacken returnera en pekare till den modierade listan (jfr insert-funktionen för binära söktrad och sorterade listor): Stack newstack(); Stack push(void *it, Stack s); Stack pop(stack s); void *top(stack s); // Konstruktor // Lagrar på stack // Tar bort från stack // Översta värdet Med användning Stack s = newstack(); s = push("hej", s); s = push("hopp", s); char *t = (char *)top(s); s = pop(s); (2008-09-15 4.3 )
Alternativt skulle vi kunna skicka en pekare till stackpekaren: Stack newstack(); // Konstruktor void push(void *it, Stack *s); // Lagrar på stack void *pop(stack *s); // Hämtar från stack Med användning Stack s = newstack(); push("hej", &s); push("hopp", &s); char *t = (char *)pop(s); Jag väljer det första alternativet (så länge) (2008-09-15 4.4 )
Deklarationsl stack.h /* stack.h - En generell stackmekanism Stacken implementeras som en länkad lista där datafältet i stackelementen är en pekare till en ospecificerad typ. */ #ifndef stack #define stack typedef struct stackelem { void *item; struct stackelem *next; stackelem, *link, *Stack; // Ospecificerad pekartyp // Pekare till efterföljare Stack newstack(); // Konstruktor Stack push(void *it, Stack s); // Lagrar på stack Stack pop(stack s); // Tar bort översta värdet int isempty(stack s); // Test om tom void* top(stack s); // "Tittar" på översta värdet på stacken #endif (2008-09-15 4.5 )
Implementationsl stack.c /* stack.c - Implementationsfil för en enkel generell stack */ #include <assert.h> #include <stdlib.h> #include "stack.h" Stack newstack() { /* Skapar och returnerar en ny, tom stack */ return NULL; ; int isempty(stack s) { /* Returnerar 1 om stacken tom, annars 0 */ return s==null; void* top(stack s) { /* Översta värdet på stacken. Pre : s - en icke tom stack Post : s - oförändrad Returnerar: det översta värdet på stacken */ assert(s); return s->item; (2008-09-15 4.6 )
Implementationsl stack.c forts Stack push(void *it, Stack s) { /* Lagrar ett nytt värde på stacken. Pre : s - initierad stack, it- godtyckligt pekarvärde. Post : it ligger överst på stacken Returnerar: Den modifierade stacken */ link l = (link) malloc(sizeof(stackelem)); l->item = it; l->next = s; return l; (2008-09-15 4.7 )
Implementationsl stack.c forts Stack pop(stack s) { /* Tar bort det översta elementet från stacken. Pre : s - en icke tom stack. Post : Det översta elementet borttaget. Returnerar: Den modifierade stacken */ assert(s); link l = s; s = s->next; free(l); return s; (2008-09-15 4.8 )
Testprogram för stack /* stactest.c - Testprogram för stackmekanismen */ #include <stdio.h> #include "stack.h" int topval(stack s) { /* Översta värdet som heltal Pre : s - stack som innehåller pekare till int. Returnerar: Översta värdet tolkar som heltal */ return * (int *)top(s); (2008-09-15 4.9 )
Testprogram för stack forts int main() { Stack s = newstack(); int t1 = 1, t2 = 2, t3 = 3, t4 = 4; double d=1.0; printf("pekare till heltal: "); s = push(&t1,s); printf("%d ", topval(s)); s = push(&t2,s); printf("%d ", topval(s)); s = pop(s); printf("%d ", topval(s)); s = push(&t3,s); printf("%d ", topval(s)); s = push(&t4,s); printf("%d ", topval(s)); t4 = 42; printf("%d\n", topval(s)); printf("\npekare till double:\n"); s = push(&d,s); printf("tolkat som double: %lf\n", topval(s)); printf("tolkat som heltal: %d\n", topval(s)); printf("\npoppa så länge det går\n"); s = pop(s); s = pop(s); s = pop(s); s = pop(s); s = pop(s); return 0; (2008-09-15 4.10 )
Testprogram för stack: utskrifter /* Testkörning: bellatrix$ gcc -o stacktest stack.c stacktest.c bellatrix$ stacktest Pekare till heltal: 1 2 1 3 4 42 Pekare till double: Tolkat som double: 1.000000 Tolkat som heltal: 1072693248 Poppa så länge det går Assertion failed: s, file stack.c, line 32 Abort bellatrix$ */ (2008-09-15 4.11 )
Användning av stack: evaluera uttryck i RPN /* rpn.c - Räknare för uttryck i omvänd polsk notation. Demonstrerar hur stack-mekanismen kan användas. */ OBS: Saknar vettig felhantering #include <stdio.h> #include <stdlib.h> #include "stack.h" /** Eget interface till stackmekanismen för att få en stack med double */ static Stack operands; double topval() { return *(double *) top(operands); // Global "privat" stack // Topp-värdet som double (2008-09-15 4.12 )
Användning av stack: evaluera uttryck i RPN forts double popval() { double *p = (double *)top(operands); double r = *p; operands = pop(operands); free(p); return r; // Poppar och returnerar toppvärdet void pushval(double x) { // Pushar double double *dp; dp = (double *)malloc(sizeof(double)); *dp = x; operands = push(dp, operands); /* slut på stackinterface */ (2008-09-15 4.13 )
Användning av stack: evaluera uttryck i RPN forts /** IO-interface */ int next() { int c; while ((c=getchar())==' '); ungetc(c,stdin); // "Lägg tillbaka" return c; /* slut på IO-interface */ (2008-09-15 4.14 )
Användning av stack: evaluera uttryck i RPN forts void perform(int c) { /* Utför operation 'c' på den globala stacken */ double y = popval(); // Obs: ordningen double x = popval(); if ( c=='+' ) pushval(x+y); else if ( c=='-' ) pushval(x-y); else if ( c=='*' ) pushval(x*y); else if ( c=='/' ) pushval(x/y); else; // Fel... (2008-09-15 4.15 )
Användning av stack: evaluera uttryck i RPN forts int main() { int c; operands = newstack(); // Initiera stacken while (1) { c = next(); if (isdigit(c)) { // Om tal så läs in och lagra det double x; scanf("%lf", &x); pushval(x); else { // annars kommando eller operator getchar(); // Läs förbi if ( c=='\n' ) printf("%f\n", topval()); else if ( c=='q' ) return 0; else { perform(c); return 0; (2008-09-15 4.16 )
Exempel: Evaluering av uttryck i inx notation Aritmetiska uttryck i vanlig, inx, notation kan (bl a) evalueras med den s k järnvägsalgoritmen. Den är baserad på två stackar - en för operatorer och en för operander. Algoritmskiss: Om tal Pusha talen på operandstacken. annars om slut på uttryck Poppa operatorstacken och utför varje operation tills operatorstacken tom. Skriv ut det enda kvarvarande värdet på stacken. annars om vänsterparentes Lägg den på stacken. annars om högerparentes Poppa och utför operationerna tills vänsterparentes hittas annars om operator Poppa operatorstacken och utför operationerna tills operatorn på stacktoppen har lägre prioritet än den aktuella operatorn. Pusha operatorn på stacken. annars fel (2008-09-15 4.17 )
Exempel: Evaluering av uttryck i inx notation /* calc.c - Kalkylator för aritmetiska uttryck i infix (vanlig) notation. Använder två stackar: en med flyttal för operander och en med heltal (tecken) för operatorer. OBS: Ingen vettig felhantering. Förutsätter att varje rad i indata innehåller ett syntaktiskt korrekt uttryck. */ #include <stdio.h> #include <stdlib.h> #include "stack.h" /** Eget interface till resultatstacken */ static Stack operands; // Global "privat" resultatstack double topval() {... double popval() {... void pushval(double x) {... /** IO-interface */ int next() {... // Som i RPN-exemplet // Som i RPN-exemplet // Som i RPN-exemplet // Som i RPN-exemplet (2008-09-15 4.18 )
Exempel: Evaluering av uttryck i inx notation forts /** Räknaren */ void perform(int c) {... // Som i RPN-exemplet int priority(int c) { /* Returnerar operatorns proritet Pre : c - teckenkod för en giltig operator Retunerar: operatorns prioritet (när den ligger på stacken) */ switch (c) { case '(': return 0; case '+': case '-': return 1; case '*': case '/': return 2; (2008-09-15 4.19 )
Exempel: Evaluering av uttryck i inx notation forts int main() { /* Parsar och evaluerar aritmetiska uttryck med den s. k. järnvägsalgoritmen */ operands = newstack(); // Initiera operandstacken Stack operators = newstack(); // Initiera operatorstacken while (1) { int oper; int c = next(); if (isdigit(c)) { // Om tal så lagra double x; scanf("%lf", &x); pushval(x); else { // annars getchar(); // Läs förbi if ( c=='\n' ) { while (!isempty(operators)) { perform((int)top(operators)); operators = pop(operators); printf("%f\n", topval()); popval(); // slut på uttryck? (2008-09-15 4.20 )
Exempel: Evaluering av uttryck i inx notation forts else if ( c=='q' ) { // eller quit? return 0; else if ( c=='(' ) { // eller '(' operators = push((void *)c,operators); // Oops! Pekare? else if ( c==')' ) { // eller ')' while ( (oper = (int)top(operators))!= '(' ) { // Oops! perform(oper); operators = pop(operators); operators = pop(operators); (2008-09-15 4.21 )
Exempel: Evaluering av uttryck i inx notation forts else { // eller operator while (!isempty(operators) && priority(c) <= priority(oper = (int)top(operators))) { //Oops! perform(oper); operators = pop(operators); operators = push((void *)c, operators); //Oops! return 0; (2008-09-15 4.22 )
Problem med den använda stackdesignen Kan man skriva en funktion som skriver ut stackens innehåll? Kan man skriva en funktion som letar efter en nod med viss egenskap? Vad är problemet? Stackmekanismen vet inte vad det är för typ på lagrade data så den vet inte hur elementen t ex skall skrivas ut (formatkoder... ) eller hur de skall jämföras om man letar efter visst innehåll. Utifrån kommer man bara åt det översta värdet. Den interna strukturen är avsiktligt dold. (2008-09-15 4.23 )
Eftersom man kan skicka funktionspekare som parameter så går det att skriva en funktion i stackmekanismen som traverserar stacken och utför en specicerad operation på alla lagrade värden. Vi skall dock välja att implementera ett annat begrepp: en iterator. En iterator är en mekanism som gör det möjligt att på ett kontrollerat sätt besöka alla lagrade element i en struktur (lista, träd, array... ). Finns inbyggd i språk som C++ och Java men i C får vi göra den själva. Vi väljer en enkel variant där man bara kan gå åt ena hållet och där man bara kan titta på värdena, inte ändra dem. (2008-09-15 4.24 )
Vi skall förutom att hålla reda på stacktoppen också hålla reda på ett aktuellt element Det medför att vi inte längre kan representera stacken utåt med en pekare till stacktoppen utan vi måste skapa en post som innehåller både pekare till toppen och pekare till aktuellt element. Utåt låter vi stacken vara representerat med en pekare till en sådan post. (2008-09-15 4.25 )
Deklarationsl för stack med iterator /* stack.h - Deklarationsfil för stack med iterator En generell stackmekanism som lagrar pekare till ospecificerad typ (void*). Stacken representeras av en post med en pekare till översta elementet och, för iteratorändamål, en pekare till aktuellt element. Själva stacken består av en länkad lista. */ #ifndef stack #define stack typedef struct stackelem { void *item; struct stackelem *next; stackelem, *link; typedef struct Stack { link top; link current; *Stack; // Listelementen // Ospecificerad pekartyp // Nästföljande element // Pekare till översta elementet // Pekare till aktuellt element (2008-09-15 4.26 )
Deklarationsl för stack med iterator forts Stack newstack(); // Konstruktor int push(void *it, Stack s); // Lagrar ett element på stacken void *pop(stack s); // Tar bort och returnerar översta elementet int isempty(stack s); // 1 om stacken tom, annars 0 void *top(stack s); // Returnerar översta elementet på stacken void iterinit(stack s); // Initierar iteratorn till översta elementet int itermore(stack s); // 1 om det finns aktuellt element, annars 0 void *iternext(stack s); // Returnerar aktuellt element och stegar fram #endif (2008-09-15 4.27 )
Implementationsl för stack med iterator /* stack.c - Imlementationsfil för stack med iterator */ #include <assert.h> #include <stdlib.h> #include "stack.h" Stack newstack() { /* Skapar en tom stack */ Stack s = (Stack) malloc(sizeof(struct Stack)); s->top = NULL; s->current = NULL; return s; (2008-09-15 4.28 )
Implementationsl för stack med iterator forts int push(void *it, Stack s) { /* Lagrar ett element överst på stacken. Pre : it - En godtycklig pekare. : s - Pekare till en initierad stack. Post : Det som it pekar på ligger överst på stacken. Returnerar: 1 om operationen lyckades, annars false */ assert(s); link l = (link) malloc(sizeof(stackelem)); if (l==null) return 0; l->item = it; l->next = s->top; s->top = l; return 1; (2008-09-15 4.29 )
Implementationsl för stack med iterator forts void* pop(stack s) { /* Tar bort och returnerar översta elementet på stacken. Pre : s - Pekare till en initierad stack. Post : Om stacken inte var tom är det översta elementet borttaget. Returnerar: NULL om stacken var tom, annars det värde som var överst. */ assert(s); if (s->top==null) return NULL; else { link l = s->top; s->top = s->top->next; void *r = l->item; free(l); return r; (2008-09-15 4.30 )
Implementationsl för stack med iterator forts int isempty(stack s) { /* Undersöker om stacken är tom. Pre : s - Pekare till initierad stack. Post : Stacken oförändrad. Returnerar: 1 om stacken tom, annars 0. */ assert(s); return s->top==null; void* top(stack s) { /* Tittar på översta elementet i stacken. Pre : s - Pekare till initierad stack. Post : Stacken oförändrad. Returnerar: Översta värdet på stacken. */ assert(s); return s->top->item; (2008-09-15 4.31 )
Implementationsl för stack med iterator forts void iterinit(stack s) { /* Initiera iteratorn. Pre : s - Pekare till initierad stack. Post : Stackens innehåll oförändrad. Översta element satt som aktuellt element. */ assert(s); s->current = s->top; int itermore(stack s) { /* Undersöka om alla elementen genomgångna. Pre : s - Pekare till initierad stack. Post : Stackens innehåll oförändrad. Returnerar: 1 om det finns ett aktuellt element, annars 0. */ assert(s); return s->current!=null; (2008-09-15 4.32 )
Implementationsl för stack med iterator forts void* iternext(stack s) { /* Stegar fram iteratorn till nästa element. Pre : s - Pekare till initierad stack. Post : Stackens innehåll oförändrad. Om det finns ett aktuellt element kommer dess efterföljare vara det nya aktuella elementet. Returnerar: Om det vid inträdet finns ett aktuellt element kommer dess värde returneras, annars NULL. */ link l = s->current; if (s->current) { s->current = s->current->next; return l->item; else { return NULL; (2008-09-15 4.33 )
Testprogram av stack med iterator /* stacktest.c - Test av stack med iterator */ #include <stdio.h> #include "stack.h" int main() { Stack s = newstack(); // Pusha tre heltal (som vore de pekare...) och poppa dem push( (void *)1, s); push( (void *)2, s); push( (void *)3, s); printf("poppade tal: "); while (!isempty(s)) { printf("%d ", (int)pop(s)); printf("\n"); (2008-09-15 4.34 )
Testprogram av stack med iterator forts // Pusha några (pekare till) strängar push( "fisken", s); push( "för", s); push( "tack", s); // Iterara genom stacken m h a iteratorn iterinit(s); printf("itererat: "); while (itermore(s)) { printf("%s ", iternext(s)); // Pusha ytterligare en sträng och poppa sedan stacken push("ajöss och", s); printf("\npoppat : "); while (!isempty(s)) { printf("%s ", pop(s)); printf("\n*slut på test*\n"); (2008-09-15 4.35 )
Utskrifter från testprogram /* Utskrift från testkörning: bellatrix$ gcc -o stacktest stack.c stacktest.c bellatrix$ stacktest Poppade tal: 3 2 1 Itererat: tack för fisken Poppat : Ajöss och tack för fisken *Slut på test* bellatrix$ */ (2008-09-15 4.36 )