CS xxxx CS 2123 Data Structures Do Quiz

  • Slides: 39
Download presentation
CS xxxx -- CS 2123 Data Structures Do Quiz 10 Do Quiz 11 Ch

CS xxxx -- CS 2123 Data Structures Do Quiz 10 Do Quiz 11 Ch 8 – Abstract Data Types STACK Turgay Korkmaz Office: NPB 3. 330 Phone: (210) 458 -7346 Fax: (210) 458 -4437 e-mail: korkmaz@cs. utsa. edu web: www. cs. utsa. edu/~korkmaz 1

Objectives n n n To appreciate the concept and purpose of abstract data types,

Objectives n n n To appreciate the concept and purpose of abstract data types, or ADTs To understand both the abstract behavior and the underlying implementation of the stack data type To be able to use incomplete type mechanism in ANSI C to define ADTs To recognize that ADTs provide an attractive alternative to maintaining encapsulated state within a module. To understand the design and implementation of a scanner abstraction based on ADTs (self-study) 2

Data Structure hierarchy n n The atomic data types—such as int, char, double, and

Data Structure hierarchy n n The atomic data types—such as int, char, double, and enumerated types—occupy the lowest level in the hierarchy. To represent more complex information, we combine the atomic types to form larger structures (e. g. , struct A {…}). These larger structures can then be assembled into even larger ones in an open-ended process using pointers. Collectively, these assemblages of information into more complex types are called data structures. atomic types records (struct {. . }) data structures (stack, list, trees, graphs) 3

Abstract data Type (ADT) n n n It is usually far more important to

Abstract data Type (ADT) n n n It is usually far more important to know how a data structure behaves rather than how it is represented or implemented A type defined in terms of its behavior rather than its representation is called an abstract data type (ADT) ADTs are defined by an interface (recall ch 08 a-ch 03) n Simplicity. Hiding the internal representation from the client means that there are fewer details for the client to understand. n n Flexibility. Because an ADT is defined in terms of its behavior, the lib programmer who implements one is free to change its underlying representation. As with any abstraction, it is appropriate to change the implementation as long as the interface remains the same so application programmer will not know the changes in lib implementation. Security. The interface boundary acts as a wall that protects the implementation and the client from each other. If a client program has access to the representation, it can change the values in the underlying data structure in unexpected ways. Making the data private in an ADT prevents the client from making such changes. 4

C Stacks n To understand the concept of ADT, we consider a specific data

C Stacks n To understand the concept of ADT, we consider a specific data structure, namely stack n n n B A a storage for a collection of data values (elements) values are removed from a stack in the opposite order from which they were added, so that the last item added to a stack is always the first item that gets removed. Adding a new element to a stack is called pushing Removing the most recent item from a stack is called popping So the defining behavior of stack is “Last in, First out” (LIFO) 5

The Stack Metaphor • A stack is a data structure in which the elements

The Stack Metaphor • A stack is a data structure in which the elements are accessible only in a last-in/first-out order. • The fundamental operations on a stack are push, which adds a new value to the top of the stack, and pop, which removes and returns the top value. • One of the most common metaphors for the stack concept is a spring-loaded storage tray for dishes. Adding a new dish to the stack pushes any previous dishes downward. Taking the top dish away allows the dishes to pop back up.

C B A Stacks turn out to be particularly useful in a variety of

C B A Stacks turn out to be particularly useful in a variety of programming applications. APPLICATIONS OF STACKS 7

Applications of Stacks n n The primary reason that stacks are important in programming

Applications of Stacks n n The primary reason that stacks are important in programming is that nested function calls behave in a stack-oriented fashion, recall Fact(n) example Compiler example: check if bracketing operators (parentheses, brackets, and curly braces) in a string are properly matched. { [ ( ) ] } Pocket calculator example from the textbook. ……. . 8

Exercise: check bracketing operators Write a C program that checks whether the bracketing operators

Exercise: check bracketing operators Write a C program that checks whether the bracketing operators (parentheses, brackets, and curly braces) in a string are properly matched. As an example of proper matching, consider the string { s = 2 * (a[2] + 3); x = (1 + (2)); } If you go through the string carefully, you discover that all the bracketing operators are correctly nested, with each open parenthesis matched by a close parenthesis, each open bracket matched by a close bracket, and so on. Operator. Matching Enter string: { s = 2 * (a[2] + 3); x = (1 + (2)); } Brackets are properly nested Enter string: (a[2] + b[3) Brackets are incorrect Enter string: If we have a Stack ADT, it will be easy to solve this problem… HOW? We will give actual imp later.

Exercise: Stacks and pocket calculators n n Suppose you want to compute 50. 0

Exercise: Stacks and pocket calculators n n Suppose you want to compute 50. 0 * 1. 5 + 3. 8 / 2. 0 In a (reverse Polish notation, or RPN) calculator, you will do this as follows: ENTER: PUSH the previous value on a stack Arithmetic operator (+ - * / ): n if the user has just entered a value push it on the stack n Apply the operator We will give n n n POP the top two values from stack Apply the arithmetic OP PUSH the result on the stack actual imp later. 10

n stack. h defines the behavior of Stack ADT (interface) n n n Export

n stack. h defines the behavior of Stack ADT (interface) n n n Export data types Export prototypes for public functions (new_stack, push, pop, etc. ) stack. c implements the public and private functions DEVELOPING A STACK ADT stack lib 11

Defining stack. h Interface #ifndef _stack_h #define _stack_h /* for comments see actual stack.

Defining stack. h Interface #ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook */ //#include "genlib. h” // #define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef double stack. Element. T; // double char, void *, etc… typedef struct stack. CDT *stack. ADT; stack. ADT New. Stack(void); void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Empty(stack. ADT stack); bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif 12

Data Types in Stack ADT n When implementing stack ADT, we need to consider

Data Types in Stack ADT n When implementing stack ADT, we need to consider two important types: n n n The type of the element stored in the stack The type of the stack data structure itself We must decide whether each type is n n part of the library implementation or part of the client’s domain/application 13

First think about stack element type n n n n For example: double in

First think about stack element type n n n n For example: double in calculator and char in bracket matching Stack element type belongs to the client’s domain/application Stack library implementation should not worry about the type of elements. All it needs to store and return an element with any type So if C had any type, we would simply use any for data elements (some languages have this – called polymorphism) But, C has no such type and requires specific types when the exported functions declare their parameters… The closest thing C provides is the type void * which is compatible with any pointer type SO, ONE SOLUTION TO DECIDING THE TYPE OF STACK ELEMENT n n Define stack element type as void * Let client application allocate memory for any element and give its pointer to stack ATD Our Stack ADT will push/pop pointers to/from stack (GREAT FLEXIBILITY) but For some applications dealing with pointers might be to complicated and inefficient! 14

stack element type (second solution) n n n If you allow client to access

stack element type (second solution) n n n If you allow client to access the source code of stack library or package, you can increase the flexibility and efficiency by using typedef In stack. h define typedef double Stack. Element. T; If client wants to use stack ADT with char type, all he/she needs to do is to change the above definition with typedef char Stack. Element. T; n n n - Client will edit stack. h (violates principle of abstraction) - Client should have the source code to compile There is no optimal design strategy. The best you can do typedef void *Stack. Element. T; 15

The type of the stack itself n n Stack type definitely belongs to the

The type of the stack itself n n Stack type definitely belongs to the library implementation of stack ADT Your implementation should be able to n push values of Stack. Element. T onto a stack n retrieve them in a LIFO order when popped To perform these operations, you need to choose a representation for stack Client should not see the implementation details or internal representations. Why? 16

Opaque type n n In stack. h, we can define an opaque type such

Opaque type n n In stack. h, we can define an opaque type such that its underlying representation is hidden from client (it is later implemented in stack. c) typedef struct name. CDT *name. ADT; For Stack ADT: n n Concrete Abstract Data Type We will have the following incomplete type in stack. h typedef struct stack. CDT *stack. ADT; We then define the concrete type in implementation stack. c struct stack. CDT { field declarations 17 }

Defining stack. h Interface #ifndef _stack_h #define _stack_h /* for comments see actual stack.

Defining stack. h Interface #ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook */ // #include "genlib. h“ // #define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef double stack. Element. T; // char, void *, etc… typedef struct stack. CDT *stack. ADT; stack. ADT New. Stack(void); void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Empty(stack. ADT stack); bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif As library developer we need to also implement stack. c For the time being, suppose we implemented stack. c So stack lib is ready to be used by applications. 18

#ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook

#ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook */ // #include "genlib. h“ // #define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef double stack. Element. T; // char, void *, etc… typedef struct stack. CDT *stack. ADT; stack. ADT New. Stack(void); void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Empty(stack. ADT stack); bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif APPLICATIONS OF STACK ADT 19

A client/driver using stack. h: /* suppose we have */ typedef char stack. Element.

A client/driver using stack. h: /* suppose we have */ typedef char stack. Element. T; bracket. c #include "stack. h" /* other libraries … */ main() { stack. ADT stack 1; char *line; char ch; int i; stack 1 = New. Stack(); printf("> "); line = Get. Line(); for(i=0; i < strlen(line); i++){ if( line[i]==‘{‘ || line[i]==‘[‘ || line[i]==‘(‘ ) push(stack 1, line[i]); if (line[i]==‘}‘ && pop(stack 1) != ‘{‘) break; if (line[i]==‘]‘ && pop(stack 1) != ‘[‘) break; if (line[i]==‘)‘ && pop(stack 1) != ‘(‘) break; } if (Stack. Is. Empty(stack 1)) printf(“Brackets are properly nestedn”); else printf(“Brackets are not properly nestedn”); Free. Stack(stack 1); } Is this working? Get. Line(); // is from the textbook’s library! How about [ …} You can implement it too, as we provided in Assing 2 It dynamically allocates memory and gets the whole input line as a String. Instead, you can also use fgets() but you have to allocate space in advance… For example: char line[256]; fgets(line, 255, stdin); > gcc bracket. c stack. c –o bracket > bracket 20

A client/driver using stack. h: rpncalc. c void Apply. Operator(char op, stack. ADT operand.

A client/driver using stack. h: rpncalc. c void Apply. Operator(char op, stack. ADT operand. Stack) { #include "stack. h" /* other libraries … */ /* suppose we have */ main() typedef double stack. Element. T; { stack. ADT operand. Stack; char *line; char ch; operand. Stack = New. Stack(); while ( 1 ) { printf("> "); line = Get. Line(); ch = toupper(line[0]); switch (ch) { case 'Q': exit(0); case 'H': Help. Command(); break; case 'C': Clear. Stack(operand. Stack); break; case 'S': Display. Stack(operand. Stack); break; default: if (isdigit(ch)) { Push(operand. Stack, String. To. Real(line)); } else { Apply. Operator(ch, operand. Stack); } break; } } double lhs, result; rhs = Pop(operand. Stack); lhs = Pop(operand. Stack); switch (op) { case '+': result = lhs + rhs; break; case '-': result = lhs - rhs; break; case '*': result = lhs * rhs; break; case '/': result = lhs / rhs; break; default: Error("Illegal operator %c", op); } printf("%gn", result); Push(operand. Stack, result); static void Clear. Stack(stack. ADT stack) { } while (!Stack. Is. Empty(stack)) { (void) Pop(stack); } static void Display. Stack(stack. ADT stack) { … } int i, depth; printf("Stack: "); depth = Stack. Depth(stack); static void Help. Command(void) {…} 21

EXERCISES 22

EXERCISES 22

Exercise: Reimplementation of Proper matching of { ( [ ] ) } n Write

Exercise: Reimplementation of Proper matching of { ( [ ] ) } n Write a C program that checks whether the bracketing operators (parentheses, brackets, and curly braces) in a string are properly matched. #ifndef _stack_h #define _stack_h n For example, { s = 2 * (a[2] + 3); x = (1 + (2)); } is a proper matching, Suppose you cannot change stack. h and it exports typedef void *stack. Element. T; Home Exercise // #include "genlib. h“ //#define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef void *stack. Element. T; typedef struct stack. CDT *stack. ADT; stack. ADT New. Stack(void); void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Empty(stack. ADT stack); bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif 23

M od ify th is A client/driver using stack. h: /* suppose we have

M od ify th is A client/driver using stack. h: /* suppose we have */ typedef void *stack. Element. T; bracket. c #include "stack. h" /* other libraries … */ main() { stack. ADT stack 1; char *line; char ch; int i; stack 1 = New. Stack(); printf("> "); line = Get. Line(); for(i=0; i < strlen(line); i++){ if( line[i]==‘{‘ || line[i]==‘[‘ || line[i]==‘(‘ ) push(stack 1, line[i]); if (line[i]==‘}‘ && pop(stack 1) != ‘{‘) break; if (line[i]==‘]‘ && pop(stack 1) != ‘[‘) break; if (line[i]==‘)‘ && pop(stack 1) != ‘(‘) break; } if (Stack. Is. Empty(stack 1)) printf(“Brackets are properly nestedn”); else printf(“Brackets are not properly nestedn”); Free. Stack(stack 1); } > gcc bracket. c stack. c > bracket 24

Exercise: Re-implementation of RPN calculator and Proper matching #ifndef _stack_h #define _stack_h n Suppose

Exercise: Re-implementation of RPN calculator and Proper matching #ifndef _stack_h #define _stack_h n Suppose you cannot change stack. h and instead of char typedef void *stack. Element. T; or double, it currently exports typedef void *stack. Element. T; Given this version of stack. h, implement RPN calc and proper matching What will be the main difference? #include "genlib. h“ // #define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef struct stack. CDT *stack. ADT; stack. ADT New. Stack(void); void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Empty(stack. ADT stack); n bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif rc ise n Ho m e Ex e n Dynamically allocate memory for each value that you push, free when you pop and/or reuse… 25

void Apply. Operator(char op, stack. ADT operand. Stack) { double lhs, result; M od

void Apply. Operator(char op, stack. ADT operand. Stack) { double lhs, result; M od ify th is A client/driver using stack. h: rpncalc. c #include "stack. h" /* other libraries … */ main() { stack. ADT operand. Stack; string line; char ch; operand. Stack = New. Stack(); while (TRUE) { printf("> "); line = Get. Line(); ch = toupper(line[0]); switch (ch) { case 'Q': exit(0); case 'H': Help. Command(); break; case 'C': Clear. Stack(operand. Stack); break; case 'S': Display. Stack(operand. Stack); break; default: if (isdigit(ch)) { Push(operand. Stack, String. To. Real(line)); } else { Apply. Operator(ch, operand. Stack); } break; } } rhs = Pop(operand. Stack); lhs = Pop(operand. Stack); switch (op) { case '+': result = lhs + rhs; break; case '-': result = lhs - rhs; break; case '*': result = lhs * rhs; break; case '/': result = lhs / rhs; break; default: Error("Illegal operator %c", op); } printf("%gn", result); Push(operand. Stack, result); static void Clear. Stack(stack. ADT stack) { } while (!Stack. Is. Empty(stack)) { (void) Pop(stack); } static void Display. Stack(stack. ADT stack) { … } int i, depth; printf("Stack: "); depth = Stack. Depth(stack); static void Help. Command(void) {…} 26

// app. c #include “stack. h” void main() { stack. ADT s 1; char

// app. c #include “stack. h” void main() { stack. ADT s 1; char ch; s 1 = New. Stack(); #ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook */ #include "genlib. h“ // #define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef char stack. Element. T; // double, char, void *, etc… typedef struct stack. CDT *stack. ADT; Push(s 1, ‘A’); Push(s 1, ‘B’); Push(s 1, ‘C’); stack. ADT New. Stack(void); ch = Pop(s 1); bool Stack. Is. Empty(stack. ADT stack); } // gcc app. c stack. c –o app void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif IMPLEMENTATION OF STACK. C 27

typedef struct stack. CDT *stack. ADT; Concrete Data Type (CDT) n First provide a

typedef struct stack. CDT *stack. ADT; Concrete Data Type (CDT) n First provide a concrete representation for abstract type stack. ADT in stack. c n Suppose we decided to use ARRAY to hold elements on the stack #define Max. Stack. Size 100 struct stack. CDT { stack. Element. T elements[Max. Stack. Size]; int count; }; n We then implement the exported (public) and private functions based on struct stack. CDT 28

stack. c #include “stack. h” /* other libraries … */ #define Max. Stack. Size

stack. c #include “stack. h” /* other libraries … */ #define Max. Stack. Size 100 struct stack. CDT { stack. Element. T elements[Max. Stack. Size]; int count; }; #include “stack. h” void main() { stack. ADT s 1; char ch; s 1 = New. Stack(); … Free. Stack(s 1); } stack. ADT New. Stack(void) { stack. ADT stack; // stack = New(stack. ADT); // stack=(struct stack. CDT *) malloc(sizeof(struct stack. CDT)); // stack=(stack. ADT) malloc(sizeof(struct stack. CDT)); } stack=(stack. ADT) malloc(sizeof *stack); if(!stack) return NULL; stack->count = 0; return (stack); void Free. Stack(stack. ADT stack) { free(stack); } 29

stack. c void Push(stack. ADT stack, stack. Element. T element) { 0 1 2

stack. c void Push(stack. ADT stack, stack. Element. T element) { 0 1 2 3 … elements count = 0 99 } #include “stack. h” void main() { stack. ADT s 1; char ch; s 1 = New. Stack(); } stack->elements[stack->count] = element; stack->count++; Push(s 1, ‘A’); ch = Pop(s 1); stack. Element. T Pop(stack. ADT stack) { stack->count--; return(stack->elements[stack->count]); } bool Stack. Is. Empty(stack. ADT stack) { return (stack->count == 0); } bool Stack. Is. Full(stack. ADT stack) { return (stack->count == Max. Stack. Size ); } 30

0 1 2 3 … 99 elements count = 0 #include “stack. h” void

0 1 2 3 … 99 elements count = 0 #include “stack. h” void main() { … Push(s 1, ‘A’); ch = Pop(s 1); } stack->elements[stack->count] = element; stack->count++; stack. c Check possible errors…. void Push(stack. ADT stack, stack. Element. T element) { if (Stack. Is. Full(stack) Error(“Stack Size exceeds”); } stack->elements[stack->count++] = element; stack. Element. T Pop(stack. ADT stack) { if (Stack. Is. Empty(stack)) Error("Pop of an empty stack"); stack->count--; return(stack->elements[stack->count]); } return (stack->elements[--stack->count]); 31

stack. c (cont’d) int Stack. Depth(stack. ADT stack) { return (stack->count); } stack. Element.

stack. c (cont’d) int Stack. Depth(stack. ADT stack) { return (stack->count); } stack. Element. T Get. Stack. Element(stack. ADT stack, int index) { if (index < 0 || index >= stack->count) { Error("Non-existent stack element"); } return (stack->elements[stack->count - index - 1]); } count elements index [5] F 0 [4] E 1 [3] D 2 [2] C 3 [1] B 4 [0] A 5 [6] 32

#ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook

#ifndef _stack_h #define _stack_h /* for comments see actual stack. h in the textbook */ #include "genlib. h“ // #define New(ptr_t) ((ptr_t) malloc(sizeof *((ptr_t) NULL))) #typedef int bool; typedef double stack. Element. T; // char, void *, etc… typedef struct stack. CDT *stack. ADT; Do Quiz 10 magic ADT stack. ADT New. Stack(void); void Free. Stack(stack. ADT stack); void Push(stack. ADT stack, stack. Element. T element); stack. Element. T Pop(stack. ADT stack); bool Stack. Is. Empty(stack. ADT stack); bool Stack. Is. Full(stack. ADT stack); int Stack. Depth(stack. ADT stack); stack. Element. T Get. Stack. Element(stack. ADT stack, int index); #endif ANOTHER IMPLEMENTATION OF STACK. C 33

Improving stack. c implementation using dynamic array while keeping stack. h as is #include

Improving stack. c implementation using dynamic array while keeping stack. h as is #include “stack. h” /* and other libraries … */ #define Initial. Stack. Size 100 struct stack. CDT { stack. Element. T *elements; int count; int size; }; // instead of #define Max. Stack. Size 100 struct stack. CDT { stack. Element. T elements[Max. Stack. Size]; int count; }; /* Prototype for Private functions, NOT exported in stack. h */ static void Expand. Stack(stack. ADT stack); stack. ADT New. Stack(void) { } stack. ADT stack; // stack = (stack. ADT) malloc(sizeof(struct stack. CDT)); stack = (stack. ADT) malloc(sizeof *stack); if(!stack) return NULL; stack->elements = malloc(Initial. Stack. Size * sizeof(stack. Element. T)); stack->count = 0; stack->size = Initial. Stack. Size; return (stack); // if NULL 34

void Push(stack. ADT stack, stack. Element. T element) { if (stack->count == stack->size) Expand.

void Push(stack. ADT stack, stack. Element. T element) { if (stack->count == stack->size) Expand. Stack(stack); stack->elements[stack->count++] = element; } stack. Element. T Pop(stack. ADT stack) { if (Stack. Is. Empty(stack)) Error("Pop of an empty stack"); 0 1 return (stack->elements[--stack->count]); } bool Stack. Is. Empty(stack. ADT stack) { return (stack->count == 0); } bool Stack. Is. Full(stack. ADT stack) { return (FALSE); } void Free. Stack(stack. ADT stack) { free(stack->elements); free(stack); } 2 3 … 99 elements = count = 0 size=100 35

int Stack. Depth(stack. ADT stack) { return (stack->count); } stack. Element. T Get. Stack.

int Stack. Depth(stack. ADT stack) { return (stack->count); } stack. Element. T Get. Stack. Element(stack. ADT stack, int index) { if (index < 0 || index >= stack->count) { Error("Non-existent stack element"); } return (stack->elements[stack->count - index - 1]); 0 1 2 } /* Private functions, NOT exported in stack. h */ static void Expand. Stack(stack. ADT stack) { elements = stack. Element. T *array; count = 0 int i, new. Size; 3 … 99 size=100 } new. Size = stack->size * 2; array == New. Array(new. Size, malloc(new. Size * sizeof(stack. Element. T)); // if NULL array stack. Element. T); for (i = 0; i < stack->count; i++) { array[i] = stack->elements[i]; } free(stack->elements); // Free. Block(stack->elements); stack->elements = array; stack->size = new. Size; 36

Do Quiz 11 Exercise n n Suppose we want to implement an opposite function

Do Quiz 11 Exercise n n Suppose we want to implement an opposite function to Expand. Stack that we call Shrink(stack). It will be called when the number of elements in stack is less than the quarter of the current size of the array and the current size of the array is larger than 200. 37

/**** stack. h is the same ****/ typedef double stack. Element. T; typedef struct

/**** stack. h is the same ****/ typedef double stack. Element. T; typedef struct stack. CDT *stack. ADT; /* …. exported functions …. . */ -----------------------/**** improved stack. c ****/ #define Initial. Stack. Size 100 struct stack. CDT{ stack. Element. T *elements; int count; int size; }; stack. Element. T Pop(stack. ADT stack) { if (Stack. Is. Empty(stack)) Error("Pop of an empty stack"); if(stack->count < stack->size / 4 && stack->size > 200) Shrink(stack); return (stack->elements[--stack->count]); } static void Shrink(stack. ADT stack) { …… } 38

struct stack. CDT { stack. Element. T elements[Max. Stack. Size]; int count; }; struct

struct stack. CDT { stack. Element. T elements[Max. Stack. Size]; int count; }; struct stack. CDT { stack. Element. T elements[Max. Stack. Size]; } static int count; OPT Danger of Encapsulated State n You can declare a global variable in a module to maintain state between functions (e. g. , randword. c, scanadt. h in previous book) n n The module that uses it can have only one copy of the state information, n n n static will make it private (encapsulated state) So we cannot use that module in different parts of the program In case of layered abstractions, client may not know anything about the underlying modules and use them in different places, resulting in error… ADT provide a safe alternative to encapsulated state 39