240 likes | 351 Views
ADT Abstract Data Types. The Stack Example. ADT. לצורך הכרות עם מבנה נתונים הממומש כ ADT ניעזר בדוגמה הבאה לאורך השיעור:. יוצגו שלושה פתרונות לבעיה זו : פתרון ישיר ופשוט - נועד להמחשת הבעיה.
E N D
ADTAbstract Data Types The Stack Example
ADT לצורך הכרות עם מבנה נתונים הממומש כ ADT ניעזר בדוגמה הבאה לאורך השיעור: יוצגו שלושה פתרונות לבעיה זו : • פתרון ישיר ופשוט - נועד להמחשת הבעיה. • פתרון אשר נעזר ב ADT של מחסנית - פתרון זה מפריד בין האפליקציה לבין מבנה הנתונים ומאפשר להיעזר במחסנית גם בתוכניות אחרות אולם מייצר מבנה נתונים המאפשר לשמור רק תווים. • פתרון אשר נעזר ב ADT גנרי - כלומר ADT המאפשר להגדיר מחסניות השומרות טיפוסי נתונים שונים – ובפרט תווים. נרצה לקרוא מהקלט הסטנדרטי 100 תווים ולהדפיסם בסדר הפוך לסדר בו נקראו.
תוכנית להיפוך 100 תווים Reverese.c #include <stdio.h> int main() { int c ; char array[100]; int cur = 0; while ((c=getchar()) != EOF && cur<100) { array[cur] = c ; cur++ ; } while (cur) printf( "%c\n", array[--cur]) ; return 0; }
הטיפוס האבסטרקטי - מחסנית • המחסנית (שנראה כעת) הנה טיפוס המאפשר שמירת מספר מסוים של תווים, כאשר בכל רגע נתון נגיש רק התו האחרון שהוכנס למחסנית (Last In First Out). • הטיפוס תומך בפעולות הבאות: • push - הוסף איבר (תו) למחסנית. • pop - הוצא את האיבר (התו) האחרון שהוכנס למחסנית (מבלי להחזיר את ערכו). • top - החזר את ערכו של האיבר (התו) האחרון שהוכנס למחסנית (מבלי להוציא אותו מהמחסנית). • count - כמה איברים (תווים) יש במחסנית. • ובנוסף: • create - צור מבנה נתונים מסוג מחסנית (ריק). • destroy - הרוס את מבנה הנתונים מחסנית. • http://www.cosc.canterbury.ac.nz/people/mukundan/dsal/StackAppl.html
טיפוס נתונים אבסטרקטי - ממשק I Stack.h #ifndef _STACK_H #define _STACK_H /* ADT of Stack of characters */ typedef struct Stack_t* Stack ; /* possible return values */ typedef enum {Fail, Success} Result ; /* IMPORTANT all functions get the stack that they should work on ! */ /* creates a Stack. sets the stack maximal capcity to max_size. If fails, returns NULL*/ Stack create(int max_size) ; /* releases the memory allocated for the stack. */ void destroy(Stack s) ;
טיפוס נתונים אבסטרקטי - ממשק II /* insert a char to the top of the stack. fails if s==NULL, or if the stack is full. */ Result push(Stack s, char c) ; /* removes the char at the top of the stack. fails if s == NULL, or if the stack is empty. */ Result pop(Stack s) ; /* returns in pc the last element that was pushed. Fails if s == NULL or pc == NULL or if the stack s is empty. */ Result top(Stack s, char* pc) ; /* returns the number of elements in the stack. Returns -1 if s == NULL. */ int count(Stack s); #endif
מימוש אפשרי של המחסנית נגדיר מבנה שבו שלושה שדות: • מערך בו ישמרו התווים. • שדה אשר ישמור את גודל המערך (וכתוצאה את מספר התווים המקסימלי שיכולים להישמר) • שדה שישמור את מספר התווים השמורים במחסנית - שהנו גם האינדקס במערך אליו יוכנס התו הבא. top ‘a’ ‘z’ ‘b’ ‘a’
טיפוס נתונים אבסטרקטי - מימוש I Stack.c #include <stdlib.h> #include "Stack.h" /* a structure that represents a Stack. one structure for each Stack */ struct Stack_t { /* The Stack is implemented as an array of characters. With top as an index to the next available position and maximal size stored in maxCapacity. */ char* array ; inttop ; intmaxCapacity ; } ;
טיפוס נתונים אבסטרקטי - מימוש II Stack create(int max_size) { Stack s ; char* tmp = NULL; if (max_size <=0) return NULL ; s = (Stack) malloc (sizeof(struct Stack_t)) ; if (s == NULL) return NULL; tmp= (char*) malloc (max_size) ; if (tmp == NULL) { free (s) ; return NULL ; } s->top = 0; s->maxCapacity = max_size ; s->array = tmp; return s; }
טיפוס נתונים אבסטרקטי - מימוש III Result push(Stack s, char c) { if ((s == NULL) || (s->top >= s->maxCapacity)) return Fail ; s->array[s->top] = c ; s->top++ ; return Success ; } Result pop(Stack s) { if ((s == NULL) || (s->top == 0)) return Fail ; s->top--; return Success ; }
טיפוס נתונים אבסטרקטי - מימוש IV Result top(Stack s, char* pc) { if ((s == NULL) || (s->top == 0) || (pc == NULL)) return Fail ; *pc = s->array[s->top-1]; return Success ; } int count(Stack s) { if ((s == NULL)) return -1; return s->top ; } Result destroy(Stack s) { if (s == NULL) return; free(s->array); free(s); }
היפוך 200 תווים תוך התחשבות בזוגיות (קודם יופיעו כל האי זוגיים בסדר הפוך ואחר כך הזוגיים בסדר הפוך) שימוש בטיפוס הנתונים האבסטרקטי #include <stdio.h> #include "Stack.h" int main() { int c ; char t ; Stack even, odd ; even=create(100) ; odd=create(100) ; while(1) { if ((c = getchar()) == EOF) break ; push(even,c); if ((c = getchar()) == EOF) break ; push(odd,c); } while (count(odd) > 0) { top(odd,&t ); putchar(t); pop(odd); } while (count(even) > 0) { top(even,&t ); putchar(t); pop(even); } return 0; } מה חסר ?
Generic ADT The stack example generic version
Generic ADT שימוש במצביעים לפונקציות ב ADT לרוב ל ADT לא משנה איזה טיפוסי נתונים יכיל, בין אם הם מחרוזות, מבנים, שלמים או תווים. בכל מקרה ה-“לוגיקה” של ה ADT תהיה זהה. למשל, מחסנית תמיד תוציא את האיברים שהוכנסו אליה בסדר הפוך לסדר ההכנסה (LIFO) - ללא קשר לטיפוס האיברים. על כן, נרצה לכתוב ADTכללי שמסוגל להכיל איברים מכל טיפוס שהוא. בדרך זו מספיק לכתוב את ה-ADT רק פעם אחת כדי שנקבל ADT שמסוגל לפעול על כל סוג של נתונים. הבעיה הנה שה-ADT לא ידע כיצד לבצע פעולות פשוטות הנוגעות לאיברים - למשל להעתיק איבר, להדפיס איבר, למחוק איבר וכדומה, שכן ביצוע פעולות אלו דורש ידיעה של סוג הטיפוס שנמצא ב-ADT. הפתרון לבעיה זו הוא שימוש במצביעים לפונקציות: המשתמש ב-ADT מעביר בעת אתחול ה-ADT מצביעים לפונקציות שמבצעות את הפעולות הספציפיות לטיפוס.
טיפוס נתונים אבסטרקטי - ממשק I Stack.h #ifndef STACK_H #define STACK_H /* ADT of a generic Stack */ typedef struct Stack_t* Stack ; typedef void* Elem; typedef Elem (*cpy_func)(Elem); typedef void (*free_func)(Elem); /* possible return values */ typedef enum {Fail, Success} Result ; /* Initialize the Stack. Sets the stack maximal capacity to max_size. Save pointers to functions. If fails, return NULL */ Stack create(int max_size, cpy_func cpy_elm, free_func free_elm ); /* Releases all the resources of the stack */ void destroy (Stack s) ;
טיפוס נתונים אבסטרקטי - ממשק II /* Insert a copy of the element to the top of the stack. Fails if s == NULL, or elm == NULL or if the stack is full. */ Result push(Stack s, Elem elm) ; /* Removes the elment at the top of the stack. Fails if s == NULL or if the stack is empty. */ Result pop(Stack s) ; /* Returns a copy of the element in the top of the stack. Fails if s == NULL or pelem == NULL. */ Result top(Stack s, Elem* pelm) ; /* Returns the number of elements in the stack. Returns -1 if s == NULL. */ int count(Stack s); #endif
טיפוס נתונים אבסטרקטי - מימוש I Stack.c #include <stdlib.h> #include "Stack.h" /* A structure that represents a Stack. One structure for each Stack */ /* Each stack keeps pointers to functions that treat elements */ struct Stack_t { Elem* array ; inttop ; intmaxCapacity ; cpy_funccp_elm; free_funcfr_elm; } ;
טיפוס נתונים אבסטרקטי - מימוש II Stack create(int max_size, cpy_func copy_elm, free_func free_elm){ Stack s ; if (max_size <=0 || free_elm==NULL || copy_elm == NULL) return NULL ; s = (Stack) malloc (sizeof(struct Stack_t)) ; if (s== NULL) return NULL; s->array = (Elem*)malloc(max_size*sizeof(Elem)) ; if (s->array == NULL) { free(s) ; return NULL ; } s->top = 0; s->maxCapacity = max_size ; s->cp_elm = copy_elm ; s->fr_elm = free_elm; return s ; }
טיפוס נתונים אבסטרקטי -מימוש III Result push(Stack s, Elem elm) { Elem tmp = NULL; if ( s == NULL || elm == NULL || s->top >= s->maxCapacity ) return Fail; tmp = s->cp_elm(elm) ; if (tmp == NULL) return Fail ; s->array[s->top] = tmp; s->top++ ; return Success ; }
טיפוס נתונים אבסטרקטי -מימוש IV Result pop(Stack s) { if ((s == NULL) || (s->top == 0)) return Fail ; s->top--; s->fr_elem(s->array[s->top]); return Success ; } Result top(Stack s, Elem* pelm) { Elem tmp = NULL; if (s == NULL || s->top == 0 || pelm == NULL) return Fail ; tmp = s->cp_elm(s->array[s->top -1]); *pelm = tmp; return (tmp == NULL) ? Fail : Success ; }
טיפוס נתונים אבסטרקטי -מימוש V int count(Stack s) { if (s == NULL) return -1; return s->top; } void destroy (Stack s) { if (s == NULL) return; while (s->top> 0 && pop(s) == Success) ; free(s->array); free(s); } האם התנאי דרוש?
שימוש בטיפוס הנתונים האבסטרקטי:היפוך 100 מחרוזות למה מחרוזות? כדי שנראה שהעבודה עם טיפוס שהוא יחסית מורכב איננה קשה במיוחד ביחס למקרה הפרטי של char. #include <stdio.h> #include <string.h> #include "Stack.h" /* functions that will be used by the ADT */ Elem cp_str(Elem s) { char *cp; if (s == NULL) return NULL; cp = (char*) malloc (strlen((char*) s) + 1); strcpy(cp, (char*) s); return cp; } void fr_str (void* s) { free (s); } בכדי שנוכל לנצל ב ADT את המצביעים לפונקציות על כולן להיות עם אותו ממשק, לכן במקום לציין את הטיפוס בו הן מטפלות כחלק מהגדרת הפונקציה, נגדירן תמיד כפונקציות המטפלות ב *void ונבצע בתוכן casting.
המשך דוגמא int main() { char str[256] ; Elem tmp; Stack st ; st = create (100, cp_str, fr_str) ; if (st == NULL) return 1 ; while(fgets(str,256,stdin) != NULL) push(st,(Elem) str); while (count(st) > 0) { top(st, &tmp) ; printf (“%s\n”, (char*) tmp); pop(st); fr_str(tmp); } destroy(st); return 0; }
דרך נוספת לבצוע אתחול מבנהשימוש במצביע למצביע Result create(Stack* ps, int max_size) { Stack s = NULL ; if ((max_size <=0) || (ps == NULL)) return Fail ; *ps = NULL; s = (Stack) malloc (sizeof(struct Stack_t)) ; if (s == NULL) return Fail; s->array = (char*) malloc (max_size) ; if (s->array == NULL) { free (s) ; return Fail ; } s->top = 0; s->maxCapacity = max_size ; *ps = s ; /* VERY IMPORTANT */ return Success ; }