720 likes | 801 Views
Polymorphism. Discrete Mathematics and Its Applications Baojian Hua bjhua@ustc.edu.cn. Variables and Types. Languages such as C, C++ or Java are called statically-typed As opposed to Basic, Lisp, etc. A relatively strict semantics on variables declaration and use
E N D
Polymorphism Discrete Mathematics and Its Applications Baojian Hua bjhua@ustc.edu.cn
Variables and Types • Languages such as C, C++ or Java are called statically-typed • As opposed to Basic, Lisp, etc. • A relatively strict semantics on variables declaration and use • Each variable must be declared with a static type • Variables declared before use • Uses should conform to types
Examples // Examples from Java: int i; i = 99; class S { int x; int y; } S pt = new S (); // compiler complains: i = pt; // Examples from C: int i; i = 99; struct s { int x; int y; }; struct s pt; // compiler complains: i = pt;
What’s Polymorphism? • A variable of polymorphic type could hold any type of values • poly: various • morphism: shapes • How to declare and use such kind of variables?
What We Want? // Suppose we have a variable x which is // polymorphic, we may write: x = 99; x = 3.14; x = “hello”; // But how to declare such a variable in // statically typed language such as C? // More specific, what x’s “type” should be? typex;
Difficulties • In C C++ or Java, the compiler automatically allocates space for every declared variable • the size of that space is calculated statically • at compile-time • the detailed data layout is determined
Difficulties // Examples: int i; // 4 bytes double f; // 8 bytes struct s { int x; int y; }; struct s pt; // 8 bytes // So it seems that we can never declare such // a polymorphic variable in C …
The Magic • The magic is that: if we want to make a variable x hold any type (size) of data d, then the only way is not to put this data d in the variable x.
p 88 Try #1 // Hummm, thus x must be a pointer (x holds some // data d, but the data d is not in x itselft--- // via an indirection). // Try #1: int *p; p = (int *)malloc (sizeof(*p)); *p = 88; // but x it seems that p could only point to // integer data (of size 4). // How to make p point to data of other size?
Try #2 // Try #2: make p point to struct data: int *p; // we want to point to a Point2d p = (int *)malloc (sizeof(struct s)); p->x = 3; p->y = 4; // Try this demo … // What happened here? p 3 4
Try #2 // Try #3: let’s cheat the compiler: int *p; p = (int *)malloc (8); ((Point2d *)p)->x = 3; ((Point2d *)p)->y = 4; // Try this demo … p
Moral • So, every pointer is essentially a polymorphic value • could point to value of any type (and size) • the trick is the ugly type conversion (cast) • of course, should be consistent • But the type “int *” is a little misleading • But recall C’s early convention (char *) • now C offers “void *” • compiler emits more meaningful error message
Void * // The use of “void *” struct s { int x; int y; }; void *p; p = malloc (sizeof (struct s)); ((struct s *)p)->x = 3; ((struct s *)p)->y = 4; // Try this demo … p
Polymorphic Data Structures • Structure: relationships • linear, tree, graph, hash, … • Data structures: relationships between data • not the data themselves • Polymorphic data structures • data are polymorphic • Next, I’ll take linear list as a running example
Linked List #1 (Integer List) typedef struct linkedListStruct *linkedList; struct linkedListStruct { int data; linkedList next; }; void insertHead (linkedList l, int data); int exists (linkedList l, int data);
l … data data data next next next Functions void insertHead (linkedList l, intdata) { linkedList t = (linkedList)malloc (sizeof (*t)); t->data = data; t->next = l->next; l->next = t; return; }
l … data data data next next next Functions int exists (linkedList l, int data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // equality test! return 1; temp = temp->next; } return 0; }
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i); } exists (list, 5); exists (list, 50);
Linked List #2 (Double List) typedef struct linkedListStruct *linkedList; struct linkedListStruct { double data; linkedList next; }; void insertHead (linkedList l, double data); int exists (linkedList l, double data);
Functions void insertHead (linkedList l, double data) { linkedList t = (linkedList)malloc (sizeof (*t)); t->data = data; t->next = l->next; l->next = t; return; }
Functions int exists (linkedList l, double data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // equality? return 1; temp = temp->next; } return 0; }
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, (double)i); } exists (list, 5.0); exists (list, 50.0);
Linked List #3 (Point List) typedef struct linkedListStruct *linkedList; struct linkedListStruct { struct s data; linkedList next; }; void insertHead (linkedList l, struct s data); int exists (linkedList l, struct s data);
Functions void insertHead (linkedList l, struct s data) { linkedList t = (linkedList)malloc (sizeof (*t)); t->data = data; t->next = l->next; l->next = t; return; }
Functions int exists (linkedList l, struct s data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // equality? return 1; temp = temp->next; } return 0; }
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, cookPoint(i, i*i)); } struct s pt1 = cookPoint (5, 5*5); struct s pt2 = cookPoint (50, 50*50); exists (list, pt1); exists (list, pt2);
Linked List #4:polymorphic list typedef void *poly; typedef struct linkedListStruct *linkedList; struct linkedListStruct { poly data; linkedList next; }; void insertHead (linkedList l, poly data); int exists (linkedList l, poly data);
Functions void insertHead (linkedList l, poly data) { linkedList t = (linkedList)malloc (sizeof (*t)); t->data = data; t->next = l->next; l->next = t; return; }
Functions int exists (linkedList l, polydata) { linkedList temp = l->next; while (temp) { if (temp->data == data) // Correct??? return 1; temp = temp->next; } return 0; }
We should turn data into a pointer, and link the pointer here! Client Code #1 #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, ???); }
Client Code #1 // “integers” list #include “linkedList.h” … linkedList list = newLinkedList (); int *p; for (int i=0; i<10; i++) { p = (int *)malloc (sizeof (*p)); *p = i; insertHead (list, p); }
Client Code #1 // “integers” list #include “linkedList.h” … linkedList list = newLinkedList (); int *p; for (int i=0; i<10; i++) { p = (int *)malloc (sizeof (*p)); *p = i; insertHead (list, p); } p = (int *)malloc (sizeof (int)); *p = 5; exists (list, p);
Client Code #2 // “doubles” list #include “linkedList.h” … linkedList list = newLinkedList (); double *p; for (int i=0; i<10; i++) { p = (double *)malloc (sizeof (*p)); *p = (double)i; insertHead (list, p); }
Client Code #3 // “point” list #include “linkedList.h” … linkedList list = newLinkedList (); struct s *p; for (int i=0; i<10; i++) { p = (struct s *)malloc (sizeof (*p)); *p = cookPoint (i, i*i); insertHead (list, p); } // The burden is shifted to the client code!
Pros. and Cons. of Polymorphism • Pros: • code reuse: write once, use in arbitrary contexts • ADT: data structures won’t change client data (won’t know) • Cons: • Inconsistency (safety issues) • Complexity • Efficiency • We’d discuss cons. issues next
Problem #1: Inconsistency (Safety Issues) #include “linkedList.h” … linkedList list = newLinkedList (); int *p; for (int i=0; i<10; i++) { p = (int *)malloc (sizeof (int)); *p = i; insertHead (list, p); } // Can we do this? double *f = (double *)listGetHeadData (list); // ever worse: void (*fp)() = (void (*)())listGetHeadData (list); fp ();
Cure to Problem #1: Inconsistency (Safety Issues) • C has no built-in static or dynamic checking against such inconsistency • Runtime error • segment fault, core dumped, • Or even worse • C programmers’ duty to prevent these! • Important: always keep invariants of our data structures in mind! • Ask yourself: what’s the type?
Problem #2: Complexity int exists (linkedList l, void *data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // Right??? return 1; temp = temp->next; } return 0; }
Equality Testing // Recall the definition of polymorphic variables: void *p, *q; // We want to write a function int equals (void *p, void *q); // to compare Contents! Not address! // How to implement this? p q
Try #1 int equals (void *p, void *q) { return (p==q); // right? } p q
Try #2 int equals (void *p, void *q) { return (*p==*q); // right? } p q
Try #3: Comparing Function Pointer as Argument typedef int (*eqTy) (void *, void *); int equals (void *p, void *q, tyEq eq) { return (eq (p, q)); } p q
Client Code int compareInt (void *p, void *q) { return *((int *)p)==*((int *)q); } //////////////////////////////////////// int *p = (int *)malloc (sizeof (*p)); *p = 9; int *q = (int *)malloc (sizeof (*q)); *q = 9; equals (p, q, compareInt);
Client Code int comparePoint2d (void *p, void *q) { return (p->x==q->x && p->y==q->y); } //////////////////////////////////////// struct s *x = (struct s *)malloc (sizeof (*x)); *x = …; struct s *y = (struct s *)malloc (sizeof (*y)); *y = …; equals (x, y, comparePoint2d); // A mimic of so-called “callback”.
Try #4: Function Pointers in Data int equals (void *p, void *q) { return (p->eq (p, q)); } p q eq
Point2d Revisited struct Point2d { int (*eq) (void *, void *); int x; int y; }; eq x y
Point2d Revisited struct Point2d *newPoint2d (int x, int y) { struct Point2d *p; p = (struct Point2d *)malloc (sizeof (*p)); p->x = x; p->y = y; p->eq = point2dEquals; return p; } p eq x y
Point2d Revisited int point2dEquals (void * pt1, void *pt2) { struct Point2d *p = (struct Point2d *)pt1; struct Point2d *q = (struct Point2d *)pt2; return ((p->x == q->x) && (p->y == q->y)); }
Try #4: Function Pointers in Data int equals (void *p, void *q) { return (p->eq (p, q)); } p q eq Commonly known as Object-oriented programming (OOP)
Problem #3: Efficiency // integer list #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i); } // “integer” list #include “linkedList.h” … linkedList list = newLinkedList (); int *p; for (int i=0; i<10; i++) { p = (int *)malloc (4); *p = i; insertHead (list, p); }