polymorphism discrete mathematics and its applications baojian hua [email protected]
Post on 19-Dec-2015
241 views
TRANSCRIPT
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 C:int i;i = 99;
struct s{
int x;int y;
};struct s pt;
// compiler complains:i = pt;
// Examples from Java:
int i;
i = 99;
class S
{
int x;
int y;
}
S pt = new S ();
// 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?
type x;
Difficulties
In C C++ or Java, the compiler automatically allocates space for every declared variable the size of that space is calculated statica
lly at compile-time
the detailed data layout is determined
Difficulties// Examples:int i; // 4 bytesdouble 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.
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?
p
88
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 polymorph
ic 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);
Functionsvoid insertHead (linkedList l, int data)
{
linkedList t = (linkedList)malloc (sizeof (*t));
t->data = data;
t->next = l->next;
l->next = t;
return;
}
data
next
data
next
data
next
l …
Functionsint exists (linkedList l, int data){ linkedList temp = l->next; while (temp) { if (temp->data == data) // equality test! return 1; temp = temp->next; } return 0;}
data
next
data
next
data
next
l …
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);
Functionsvoid insertHead (linkedList l, double data)
{
linkedList t = (linkedList)malloc (sizeof (*t));
t->data = data;
t->next = l->next;
l->next = t;
return;
}
Functionsint 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);
Functionsvoid insertHead (linkedList l, struct s data)
{
linkedList t = (linkedList)malloc (sizeof (*t));
t->data = data;
t->next = l->next;
l->next = t;
return;
}
Functionsint 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 listtypedef void *poly;typedef struct linkedListStruct *linkedList;struct linkedListStruct{ poly data; linkedList next;};
void insertHead (linkedList l, poly data);int exists (linkedList l, poly data);
Functionsvoid insertHead (linkedList l, poly data)
{
linkedList t = (linkedList)malloc (sizeof (*t));
t->data = data;
t->next = l->next;
l->next = t;
return;
}
Functionsint exists (linkedList l, poly data){ linkedList temp = l->next; while (temp) { if (temp->data == data) // Correct??? return 1; temp = temp->next; }
return 0;}
Client Code #1#include “linkedList.h”
…
linkedList list = newLinkedList ();
for (int i=0; i<10; i++) {
insertHead (list, ???);
}
We should turn data into a pointer, and link the pointer here!
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: Complexityint 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 functionint equals (void *p, void *q);// to compare Contents! Not address!// How to implement this?
p q
Try #1int equals (void *p, void *q){ return (p==q); // right?}
p q
Try #2int equals (void *p, void *q){ return (*p==*q); // right?}
p q
Try #3: Comparing Function Pointer as Argumenttypedef int (*eqTy) (void *, void *);
int equals (void *p, void *q, tyEq eq){ return (eq (p, q));}
p q
Client Codeint 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 Codeint 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 Dataint equals (void *p, void *q){ return (p->eq (p, q));}
p q
eq
Point2d Revisitedstruct Point2d
{
int (*eq) (void *, void *);
int x;
int y;
};
eqx
y
Point2d Revisitedstruct 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;
}
eqx
y
p
Point2d Revisitedint 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 Dataint 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 = newLinke
dList ();
for (int i=0; i<10; i++) { insertHead (list, i);}
// “integer” list#include “linkedList.h”…linkedList list = newLinke
dList ();int *p;for (int i=0; i<10; i++) { p = (int *)malloc (4); *p = i; insertHead (list, p);}
Boxed Data Polymorphism does not come free
data mostly 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 difficulty of reallocation popularized the
technology of garbage collection
Extension to other Languages Two flavors of polymorphism so far:
ad-hoc void *
They are important in that they motivate ideas for “advanced” features in other language Next, we’d discuss how C++ and Java
reflect this observation and I’ll also use linked list as running
example After that, you should be able to give some
comment
Case Study
Essentially, in C++ or Java, we could also use some form of “void *” C++ even supports that directly
But we concentrate on: C++ Template Java Generic
Linked List Template#ifndef LINKED_LIST_H#define LINKED_LIST_H
template <typename X>class LinkedList{public:
X data;LinkedList<X> *next;
void insertHead (X data);int exists (X data);
};// function on next slides…#endif
… and Functions// Note: these code is in same .h file as above!template <typename X>void LinkedList<X>::insertHead (X data){
LinkedList<X> *temp = new LinkedList<X> ();temp->data = data;temp->next = this->next;this->next = temp;
return;}
… and Functions// Note: these code is in same .h file as above!template <typename X>int LinkedList<X>::exists (X data){
LinkedList<X> *temp = this->next;while (temp) { if (temp->data == data) return 1; temp = temp->next;}
return 0;}
Client Code#include “linkedList.h”
int main (){ LinkedList<int> *p = …; LinkedList<double> *q = …;
for (int i=0; i<10; i++) p->insertHead (i);
p->exists (5);q->exists (3.14);
}
What’s a Template?template <typename X>class LinkedList{public:
X data;LinkedList<X> *next;
}; // which results in (roughly):
class LinkedList_int{public: int data; LinkedList_int *next;};
class LinkedList_double{public: double data; LinkedList_double *next;};
Static Monomorphization Program code are duplicated
Exponential in theory (code blowing) Seldom observed in practice
Separate compilation is lost The template’s interface can not be
compiled alone and the implementation is exposed
Template Rethink the ad-hoc poly in C
Any difference? Rethink the problem of poly in C, does
template also incur these problems? Why or why not? Security (inconsistency) Complexity Efficiency
Case Study
C++ Template Java Generic
void * ≈ Object
// In Cvoid *p;
p = (int *)malloc (sizeof(int));*p = 99;…
printf (“%d\n”, *p);
equals (p, q, intEq);
// In Java
Object p;
p = new Integer (99);
…
System.out.println
(((Integer)p).intValue ());
p.equals (q);
Linked List in Javaclass LinkedList
{
Object data;
LinkedList next;
void insert (Object data)
{…}
Object getFirst ()
{…}
}
Client CodeLinkedList 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)LinkedList list = new LinkedList ();
for (int i=0; i<10; i++) list.insert (new Integer (i));
// compile-time errorString s = list.getFirst ();
Problem #1: Inconsistency (Safety Issues)LinkedList list = new LinkedList ();
for (int i=0; i<10; i++) list.insert (new Integer (i));
// shut up the compiler, but raise run-time // exception:String s = (String)list.getFirst ();
Cure to Problem #1: Genericclass LinkedList<X> { X data; LinkedList next;
void insert (X data) {…}
X getFirst () {…}}
Use of GenericLinkedList<Integer> list = new LinkedList<Integer> ();
for (int i=0; i<10; i++) list.insert (new Integer (i));
// compile-time errorlist.insert (new String (“hello”));
Cure to Problem #1: Use of GenericLinkedList<Integer> list = new LinkedList<Integer> ();
for (int i=0; i<10; i++) list.insert (new Integer (i));
// compile-time errorlist.insert (new String (“hello”));
// compile-time errorString s = list.getFirst ();
Problem #2: Complexity// Turn back to “equals ()” functionint equals (Object p, Object q);// How to implement this?
p q
Cure to Problem #2: Dynamic Method Dispatch
Every class has an “equals” method, the call to “equals” is automatically
dispatched to the correct one in the current called object. (virtual functions)
p q
eq
Problem #3: Efficiency
Nearly all data in Java are boxed objects typically heap-allocated
Rely on garbage collection to recycle dead objects user have little control on this used in embedded system?