590 likes | 685 Views
Polymorphism. Discrete Mathematics and Its Applications Baojian Hua bjhua@ustc.edu.cn. Variables and Types. Languages such as C, C++ or Java have a relatively strict semantics to variables declaration and use Each variable has a static (declared) type Variables declared before 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 have a relatively strict semantics to variables declaration and use • Each variable has a static (declared) type • Variables declared before use
Examples // Examples from C: int i; i = 99; double f; f = 3.14; // compiler complains i = f; // Examples from Java: class A{} class B{} A a; a = new A (); // compiler complains a = new B();
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 a variable x is polymorphic, we want // to write: x = 99; x = 3.14; x = “hello”; // But how to declare such a variable in // statically typed language as C? // More specific, what the “type” of x should be? type x;
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 Point2d { int x; int y; } struct Point2d s; // 8 bytes // So it seems that we can never declare such // kind of a 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 The Magic // Hummm, thus x must be a pointer (x holds some // data d, but the data d is not in x---an // indirection). // Try #1: int *p; p = malloc (sizeof(int)); *p = 88; // but x could only points to data of size 4 // How to make p point to data of other size?
The Magic // Try #2: make p point to float data: int *p; // we want to point to a Point2d p = malloc (8); p->x = 3; p->y = 4; // Try this demo … // What happened here? p 3 4
Cheat the Compiler // Try #3: let’s cheat the compiler: int *p; p = 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 (size) • the trick is ugly type conversion (cast) • of course, should be consistent • Every time we want a polymorphic variable, we declare an arbitrary pointer type • But the “int *” is a little misleading • 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
Linear List (Linked-based) // a list of integers: typedef struct linkedListStruct *linkedList; struct linkedListStruct { int data; linkedList next; }; void insertHead (linkedList l, int data); int exists (linkedList l, int data);
Linear List (Linked-based) void insertHead (linkedList l, int data) { linkedList temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; }
Linear List (Linked-based) int exists (linkedList l, int data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // note 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, i); }
Linear List (Linked-based) // another linked list of doubles: typedef struct linkedListStruct *linkedList; struct linkedListStruct { double data; linkedList next; }; void insertHead (linkedList l, double data); int exists (linkedList l, double data);
Linear List (Linked-based) void insertHead (linkedList l, double data) { linkedList temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; } // See? Code duplicated!
Linear List (Linked-based) int exists (linkedList l, double data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // note equality! return 1; temp = temp->next; } return 0; } // See? Code duplicated!
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i+0.0); }
Linear List (Linked-based) // a polymorphic linked list: typedef struct linkedListStruct *linkedList; struct linkedListStruct { void*data; linkedList next; }; void insertHead (linkedList l, void *data); int exists (linkedList l, void *data);
Linear List (Linked-based) void insertHead (linkedList l, void *data) { linkedList temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; }
Linear List (Linked-based) int exists (linkedList l, void *data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // Right??? return 1; temp = temp->next; } return 0; }
We should turn data d into a pointer p, and link p here! Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, ???); }
Client Code // a list of “integers” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (sizeof (int)); *((int *)p) = i; insertHead (list, p); }
Client Code // a list of “doubles” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (sizeof (double)); *((double *)p) = i+0.0; insertHead (list, p); } // The burden is lifted to user of linkedList!
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 (); void *p; for (int i=0; i<10; i++){ p = malloc (sizeof (int)); *((int *)p) = i; insertHead (list, p); } double *f = (double *) listGetHeadData (list); // ever worse: typdef void (*funTy) (); funTy fp = (funTy) 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 • In C. It’s programmers’ duty to guarantee this! • Important: always keep invariants of our data structures in mind!
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; }
Problem #2: Complexity // Recall the definition of polymorphic variables: void *p, *q; // We want to write a function “equals ()” int equals (void *p, void *q); // How to implement this? p q
Problem #2: Complexity // Try #1: int equals (void *p, void *q) { return (p==q); // right? } p q
Problem #2: Complexity // Try #2: int equals (void *p, void *q) { return (*p==*q); // right? } p q
Cure to Problem #2: Extra Comparing Function // Try #2: typedef int (*eqTy) (void *, void *); int equals (void *p, void *q, tyEq eq) { return (eq (p, q)); } p q
Client Code int compInt (void *p, void *q){ return *((int *)p)==*((int *)q); } //////////////////////////////////////// void *p = malloc (sizeof (int)); *((int *)p) = 9; void *q = malloc (sizeof (int)); *((int *)q) = 9; equals (x, y, compInt);
Client Code int compPoint2d (void *p, void *q){ …; return (p->i==q->i && p->f==q->f); } //////////////////////////////////////// void *x = malloc (sizeof (struct Point2d)); *x = …; void *y = malloc (sizeof (struct Point2d)); *y = …; equals (x, y, compPoint2d); // A mimic of so-called “callback”.
Cure to Problem #2: Function Pointers in Data int equals (void *p, void *q) { return (p->eq (p, q)); } p q eq Essential features of OO programming!
Problem #3: Efficiency // a list of integers #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i); } // a list of “integers” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insertHead (list, p); }
Boxed Data • Polymorphism does not come free • data must be heap-allocated, to cope with the “void *” pointer convention • makes memory management expensive • It’s programmers’ duty to recycle garbage • Such kind of data are called “boxed” • and “void *” is essentially a mask • popularize the technology of garbage collection
Case Study • Java Generics • C++ Templates
The Hack Applies to Java // In C void *p; p = malloc(sizeof(int)); *((int *)p) = 99; … printf (“%d\n”, *((int *)p)); equals (p, q, eq); // In Java Object p; p = new Integer (99); … System.out.println (((Integer)p).intValue ()); p.equals (q);
Linked List in Java class LinkedList { Object data; LinkedList next; void insert (Object data) {…} Object getFirst () {…} }
Client Code import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // Also Ok for a list of strings: for (int i=0; i<10; i++) list.insert (new String (“hello”));
Problem #1: Inconsistency (Safety Issues) import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // compile-time error String s = list.getFirst ();
Problem #1: Inconsistency (Safety Issues) import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // run-time exception String s = (String)list.getFirst ();
Cure to Problem #1: Generic class LinkedList<X> { X data; LinkedList next; void insert (X data) {…} X getFirst () {…} }
Cure to Problem #1: Use of Generic import util.LinkedList; LinkedList<Integer> list = new LinkedList<Integer> (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // compile-time error list.insert (new String (“hello”));
Cure to Problem #1: Use of Generic import util.LinkedList; LinkedList<Integer> list = new LinkedList<Integer> (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // compile-time error list.insert (new String (“hello”)); // compile-time error String s = list.getFirst ();