540 likes | 563 Views
Polymorphism. Discrete Mathematics and Its Applications Baojian Hua bjhua@ustc.edu.cn. Variables and Types. Languages such as C or Java have a relative strict semantics to variables use Each variable has a static (declared) type Variables declared before use. Examples.
E N D
Polymorphism Discrete Mathematics and Its Applications Baojian Hua bjhua@ustc.edu.cn
Variables and Types • Languages such as C or Java have a relative strict semantics to variables 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 polymorphic typed variable 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? // What the “type” of x should be? type x;
Difficulties • In C or Java, compiler automatically allocates space for every declared variable • And the size of that space is calculated statically (at compile-time).
Difficulties // Examples: int i; // 4 bytes double f; // 8 bytes struct pt2d { int x; double y; } s; // 12 types // 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, then the only way is not to put this data in that variable x.
x 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 *x; x = malloc (sizeof(int)); *x = 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 = malloc (8); *x = 3.14; // Try this demo … // What happened here? *x = 3.14; x
The Magic // Try #3: let’s cheat the compiler: int *x; x = malloc (8); *((double *)x) = 3.14; // Try this demo … x
The Magic // Try #4: a structure: struct s { int i; double f; }; int *x; x = malloc (sizeof (struct s)); ((struct s *)x)->i = 3; ((struct s *)x)->f = 3.14; // Try this demo … x
Moral • So, every pointer is essentially a polymorphic value • 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 • C’s early convention (char *) • now it offers “void *” • compiler emits more meaningful error message
Void * // The use of “void *” struct s { int i; double f; }; void *x; x = malloc (sizeof (struct s)); ((struct s *)x)->i = 3; ((struct s *)x)->f = 3.14; // Try this demo … x
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 linkedList *linkedList; struct linkedList { int data; linkedList next; }; void insertHead (linkedList l, int data); int lookup (linkedList l, int data);
Linear List (Linked-based) // implementation of “insertHead” 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) // implementation of “lookup” int lookup (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 linkedList *linkedList; struct linkedList { double data; linkedList next; }; void insertHead (linkedList l, double data); int lookup (linkedList l, double data);
Linear List (Linked-based) // implementation of “insertHead” 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) // implementation of “lookup” int lookup (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, 3.14); }
Linear List (Linked-based) // a polymorphic linked list: typedef struct linkedList *linkedList; struct linkedList { void*data; linkedList next; }; void insertHead (linkedList l, void *data); int lookup (linkedList l, void *data);
Linear List (Linked-based) // implementation of “insertHead” 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) // implementation of “lookup” int lookup (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 (4); *((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 (8); *((double *)p) = 3.14; insertHead (list, p); } // The burden is lifted to user of linkedList!
Pros. and Cons. of Polymorphism • Polymorphism pros: • code reuse: write once, use in arbitrary contexts • ADT: data structures won’t change client data (won’t know) • Polymorphism 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 (4); *((int *)p) = i; insertHead (list, p); } double *f = listGetHead (list); // ever worse: int (*p)() = listGetHead (list); (*p) ();
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 • In C. It’s programmers’ duty to guarantee this! • Important: always keep your data structure invariants in mind!
Problem #2: Complexity // implementation of “lookup” int lookup (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 *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 (*tyEq) (void *, void *); int equals (void *x, void *y, tyEq 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 “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 harder • 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
A Glimpse on Other Forms of Polymorphism // In C void *p; p = malloc (4); *((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)); // Or: 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 ();