Polymorphism Discrete Mathematics and Its Applications Baojian Hua
Polymorphism Discrete Mathematics and Its Applications Baojian Hua bjhua@ustc. edu. cn
Variables and Types n Languages such as C or Java have a relative strict semantics to variables use n n Each variable has a static (declared) type Variables declared before use
Examples // Examples from C: int i; i = 99; // Examples from Java: class A{} class B{} double f; f = 3. 14; A a; a = new A (); // compiler complains i = f; // compiler complains a = new B();
What’s Polymorphism? n A polymorphic typed variable could hold any type of values n n n 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? // What the “type” of x should be? type x;
Difficulties n In C or Java, compiler automatically allocates space for every declared variable n And the size of that space is calculated statically (at compile-time).
Difficulties // Examples: int i; double f; // 4 bytes // 8 bytes struct pt 2 d { int x; double y; } s; // 12 types // So it seems that we can never declare such // kind of a variable in C …
The Magic n The magic is that: if we want to make a variable x hold any type (size) of data, then the only way is not to put this data in that variable x.
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 *x; x x = malloc (sizeof(int)); *x = 88; 88 // but x could only points to data of size 4 // How to make x point to other sized data?
The Magic // Try #2: make x point to float data: int *x; x x = malloc (8); *x = 3. 14; // Try this demo … // What happened here? *x = 3. 14;
The Magic // Try #3: let’s cheat the compiler: int *x; x x = malloc (8); *((double *)x) = 3. 14; // Try this demo …
The Magic // Try #4: a structure: struct s { int i; double f; }; x int *x; x = malloc (sizeof (struct s)); ((struct s *)x)->i = 3; ((struct s *)x)->f = 3. 14; // Try this demo …
Moral n So, every pointer is essentially a polymorphic value n n could point to any type (size) of value the trick is ugly type conversion Every time we want a polymorphic variable, we declare an arbitrary pointer type But the “int *” is a little misleading n n C’s early convention (char *) now it offers “void *” n compiler emits more meaningful error message
Void * // The use of “void *” struct s { int i; double f; }; x void *x; x = malloc (sizeof (struct s)); ((struct s *)x)->i = 3; ((struct s *)x)->f = 3. 14; // Try this demo …
Polymorphic Data Structures n Structure: relationships n n Data structures: relationships between data n n not the data themselves Polymorphic data structures n n linear, tree, graph, hash, … data are polymorphic Next, I’ll take linear list as a running example
Linear List (Linked-based) // a list of integers: typedef struct linked. List *linked. List; struct linked. List { int data; linked. List next; }; void insert. Head (linked. List l, int data); int lookup (linked. List l, int data);
Linear List (Linked-based) // implementation of “insert. Head” void insert. Head (linked. List l, int data) { linked. List temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; }
Linear List (Linked-based) // implementation of “lookup” int lookup (linked. List l, int data) { linked. List temp = l->next; while (temp) { if (temp->data == data) // note equality! return 1; temp = temp->next; } return 0; }
Client Code #include “linked. List. h” … linked. List list = new. Linked. List (); for (int i=0; i<10; i++) { insert. Head (list, i); }
Linear List (Linked-based) // another linked list of doubles: typedef struct linked. List *linked. List; struct linked. List { double data; linked. List next; }; void insert. Head (linked. List l, double data); int lookup (linked. List l, double data);
Linear List (Linked-based) // implementation of “insert. Head” void insert. Head (linked. List l, double data) { linked. List temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; } // See? Code duplicated!
Linear List (Linked-based) // implementation of “lookup” int lookup (linked. List l, double data) { linked. List temp = l->next; while (temp) { if (temp->data == data) // note equality! return 1; temp = temp->next; } return 0; } // See? Code duplicated!
Client Code #include “linked. List. h” … linked. List list = new. Linked. List (); for (int i=0; i<10; i++) { insert. Head (list, 3. 14); }
Linear List (Linked-based) // a polymorphic linked list: typedef struct linked. List *linked. List; struct linked. List { void *data; linked. List next; }; void insert. Head (linked. List l, void *data); int lookup (linked. List l, void *data);
Linear List (Linked-based) // implementation of “insert. Head” void insert. Head (linked. List l, void *data) { linked. List temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; }
Linear List (Linked-based) // implementation of “lookup” int lookup (linked. List l, void *data) { linked. List temp = l->next; while (temp) { if (temp->data == data) // Right? ? ? return 1; temp = temp->next; } return 0; }
Client Code #include “linked. List. h” … linked. List list = new. Linked. List (); for (int i=0; i<10; i++) { insert. Head (list, ? ? ? ); } We should turn data d into a pointer p, and link p here!
Client Code // a list of “integers” #include “linked. List. h” … linked. List list = new. Linked. List (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insert. Head (list, p); }
Client Code // a list of “doubles” #include “linked. List. h” … linked. List list = new. Linked. List (); void *p; for (int i=0; i<10; i++) { p = malloc (8); *((double *)p) = 3. 14; insert. Head (list, p); } // The burden is lifted to user of linked. List!
Pros. and Cons. of Polymorphism n Polymorphism pros: n n n Polymorphism cons: n n code reuse: write once, use in arbitrary contexts ADT: data structures won’t change client data (won’t know) Inconsistency (safety issues) Complexity Efficiency We’d discuss cons. issues next
Problem #1: Inconsistency (Safety Issues) #include “linked. List. h” … linked. List list = new. Linked. List (); void *p; for (int i=0; i<10; i++){ p = malloc (4); *((int *)p) = i; insert. Head (list, p); } double *f = list. Get. Head (list); // ever worse: int (*p)() = list. Get. Head (list); (*p) ();
Cure to Problem #1: Inconsistency (Safety Issues) n C has no built-in static or dynamic checking against such inconsistency n Runtime error n n segment fault, core dumped In C. It’s programmers’ duty to guarantee this! n Important: always keep your data structure invariants in mind!
Problem #2: Complexity // implementation of “lookup” int lookup (linked. List l, void *data) { linked. List 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 *x, void *y); // How to implement this? x y
Problem #2: Complexity // Try #1: int equals (void *x, void *y) { return (x==y); // right? } x y
Problem #2: Complexity // Try #2: int equals (void *x, void *y) { return (*x==*y); // right? } x y
Cure to Problem #2: Extra Comparing Function // Try #2: typedef int (*ty. Eq) (void *, void *); int equals (void *x, void *y, ty. Eq eq) { return (eq (x, y)); } x y
Client Code int comp (void *p, void *q){ return (*p==*q); } //////////////////// void *x = malloc (4); *x = 9; void *y = malloc (4); *y = 9; equals (x, y, comp);
Client Code int comp (void *p, void *q){ return (p->i==q->i && p->f==q->f); } //////////////////// void *x = malloc (sizeof (struct s)); *x = …; void *y = malloc (sizeof (struct s)); *y = …; equals (x, y, comp); // A mimic of so-called “callback”.
Cure to Problem #2: Function Pointers in Data int equals (void *x, void *y) { return (x->eq (x, y)); } x y eq Essential features of OO programming!
Problem #3: Efficiency // a list of integers #include “linked. List. h” … linked. List list = new. Linked. List (); for (int i=0; i<10; i++) { insert. Head (list, i); } // a list of “integers” #include “linked. List. h” … linked. List list = new. Linked. List (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insert. Head (list, p); }
Boxed Data n Polymorphism does not come free n n data must be heap-allocated, to cope with the “void *” pointer convention makes memory management harder It’s programmers’ duty to recycle garbage Such kind of data are called “boxed” n n and “void *” is essentially a mask popularize the technology of garbage collection
A Glimpse on Other Forms of Polymorphism // In C void *p; // In Java Object p; p = malloc (4); *((int *)p) = 99; … p = new Integer (99); … printf (“%dn”, *((int *)p)); System. out. println (((Integer)p). int. Value ()); equals (p, q, eq); p. equals (q);
Linked List in Java class Linked. List { Object data; Linked. List next; void insert (Object data) {…} Object get. First () {…} }
Client Code import util. Linked. List; Linked. List list = new Linked. List (); for (int i=0; i<10; i++) list. insert (new Integer (i)); // Or: for (int i=0; i<10; i++) list. insert (new String (“hello”));
Problem #1: Inconsistency (Safety Issues) import util. Linked. List; Linked. List list = new Linked. List (); for (int i=0; i<10; i++) list. insert (new Integer (i)); // compile-time error String s = list. get. First ();
Problem #1: Inconsistency (Safety Issues) import util. Linked. List; Linked. List list = new Linked. List (); for (int i=0; i<10; i++) list. insert (new Integer (i)); // run-time exception String s = (String)list. get. First ();
Cure to Problem #1: Generic class Linked. List<X> { X data; Linked. List next; void insert (X data) {…} X get. First () {…} }
Cure to Problem #1: Use of Generic import util. Linked. List; Linked. List<Integer> list = new Linked. List<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. Linked. List; Linked. List<Integer> list = new Linked. List<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. get. First ();
Problem #2: Complexity // Turn back to “equals ()” function int equals (Object y); // How to implement this? x y
Cure to Problem #2: Dynamic Method Dispatch n Every class has an “equals” method, n the call to “equals” is automatically dispatched to the correct ones in the current called objects. x y eq
Problem #3: Efficiency // a list of integers #include “linked. List. h” … linked. List list = new. Linked. List (); for (int i=0; i<10; i++) { insert. Head (list, i); } // a list of “integers” #include “linked. List. h” … linked. List list = new. Linked. List (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insert. Head (list, p); }
Problem #3: Efficiency n Nearly all data in Java are boxed n n typically heap-allocated in all compilers space automatically allocated by the compilers transparent to the programmers Rely on garbage collection to recycle dead objects
- Slides: 54