more on abstract data types noah mendelsohn tufts university email:...
TRANSCRIPT
More on Abstract Data Types
Noah MendelsohnTufts UniversityEmail: [email protected]: http://www.cs.tufts.edu/~noah
COMP 40: Machine Structure and
Assembly Language Programming (Spring 2014)
© 2010 Noah Mendelsohn
Last time we covered
How data and pointers are stored in the computer’s memory
Abstraction, modularity, reuse, information hiding
The special role of void * and incomplete structures
Generalizing over values and types
2
Today Generalizing over computation
Function pointers
Iteration and mapping in a functional style
Boxed and unboxed data structures
© 2010 Noah Mendelsohn
Software structures model real world objects and concepts
Integers
Students
Bank statements
Photographic images
Sound recordings
Etc.
4
These things aren’t bits!!
They don’t live in computers, but…
© 2010 Noah Mendelsohn
Software structures model real world objects and concepts
Integers
Students
Bank statements
Photographic images
Sound recordings
Etc.
5
We build data structures that model them
The course materials refer to these is being in the “World of Ideas”
© 2010 Noah Mendelsohn
Key principles to watch for
Abstraction – Refers to the act of “pulling away from” something so that unimportant details
are hidden”1
Modularity & clean interfaces– Each module “does one thing well” 2
Information hiding– Modules keep details of their internals secret
Generalization– When practical, each module or service should be as generally useful as
possible
6
1 Prof. Mark Sheldon – COMP 11 Big Ideas 2 Ken Thompson, co-inventor of Unix – The Unix Philosophy
© 2010 Noah Mendelsohn
Interfaces
7
Inte
rface
ImplementationClient
E.g. your fgroups program
E.g. implementation ofHanson Table_T
E.g. methods in Hanson table.h
© 2010 Noah Mendelsohn
Interfaces
8
Inte
rface
ImplementationClient
Many different programs can re-use the interface and the implementation!
The implementation chooses a representation … NOT visible to the client
© 2010 Noah Mendelsohn
Interfaces
9
Inte
rface
ImplementationClient
E.g. your fgroups program
E.g. implementation ofHanson Table_T
E.g. methods in Hanson table.h
My_table = Table_new(… … …)
Q. What can Table_new return?
I will show you two ways of doing this
A. A pointer to data you can’t look at
You will use this pointer to identify the new table
© 2010 Noah Mendelsohn
Client doesn’t know secret implementation
10
Inte
rface
ImplementationClient
my_table = Table_new(… … …)
Struct Table_t { …data declartions…}
struct Table_T *table_ptr;
void *table_ptr;
© 2010 Noah Mendelsohn
Client doesn’t know secret implementation
12
Inte
rface
ImplementationClient
my_table = Table_new(… … …)
Struct Table_t { …data declartions…}
struct Table_T *table_ptr;
void *table_ptr;
Note: These declarations are in the declaration
(either in a table.c, or in a tableimp.h)
© 2010 Noah Mendelsohn
Hanson does this with incomplete structs
13
Inte
rface
ImplementationClient
my_table = Table_new(… … …)
Struct Table_t { …data declartions…}
struct Table_T *table_ptr;
struct Table_t *table_ptr;
Client has incomplete
declaration of the struct
© 2010 Noah Mendelsohn
By the way, this is exactly what languages like C++ do
14
class Myclass { int some_method(int a) {…};}
Myclass my_object = new Myclass();
my_object -> some_method(5);
Under the covers, C++ implements this as:
my_object -> some_method(my_object, 5);
© 2010 Noah Mendelsohn
You’ve already seen some generalization
16
int square3() {
return 3*3;}
We don’t do this
int square(int n) {
return n*n;}
We do this!
Generalize over input value
Can we generalize over the type of information?
© 2010 Noah Mendelsohn
We need to generalize over types
17
List_of_students_new(…);List_of_cars_new(…);List_of_bank_stmts_new(…);
We don’t want this
List_new(…);
We want this!
How do we declare the input to List_push()?(after all, its type could be anything)
Can we generalize over the type of information?
© 2010 Noah Mendelsohn
How do we declare List_push?
18
void List_push(List_T list, void *x);
Hanson’s declaration for List_push
struct Car {char *brand; int weight};typedef struct Car Car;Car mycar = {“ford”, 2000};Car *retrieved_car;
mylist = List_list(NULL); /* create empty list */List_push(mylist, &mycar); /* put my car on the list */
© 2010 Noah Mendelsohn
Void * allows us to generalize over types
19
void List_push(List_T list, void *x);
Hanson’s declaration for List_push
The list will remember a pointer to anything.
struct Car {char *brand; int weight};typedef struct Car Car;Car mycar = {“ford”, 2000};Car *retrieved_car;
mylist = List_list(NULL); /* create empty list */List_push(mylist, &mycar); /* put my car on the list */
Any pointer can be passed to a void * parameter
© 2010 Noah Mendelsohn
Void * allows us to generalize over types
20
void List_push(List_T list, void *x);
Hanson’s declaration for List_push
struct Car {char *brand; int weight};typedef struct Car Car;Car mycar = {“ford”, 2000};Car *retrieved_car_p;
mylist = List_list(NULL); /* create empty list */List_push(mylist, &mycar); /* put my car on the list */
List_pop(mylist, &retrieved_car_p); /* get back mycar */
This generalization is known as universal polymorphism
© 2010 Noah Mendelsohn
Void * allows us to generalize over types
21
void List_push(List_T list, void *x);
Hanson’s declaration for List_push
struct Car {char *brand; int weight};typedef struct Car Car;Car mycar = {“ford”, 2000};Car *retrieved_car_p;
mylist = List_list(NULL); /* create empty list */List_push(mylist, &mycar); /* put my car on the list */
List_pop(mylist, &retrieved_car_p); /* get back mycar */
IMPORTANT:Retrieved_car_p is already a pointer. Why do we have to pass the address of retrieved_car_p?
© 2010 Noah Mendelsohn
Why generalize over function?
We’ve already generalized types…sometimes we need different code to work on each type– Classic example: we’re building a sort routine that can sort anything…
– …the rules for sorting different types of things are different
Mapping: do the same operation on each entry in a collection
Providing or overriding behavior in a common implementation– Overriding default output format
– Overriding data source for some input routine
– Etc.
23
© 2010 Noah Mendelsohn
Function pointers in C
We know the code is living in memory
Can we take the address of code? Yes!
24
Syntax
int square(int n) {
return n*n;}
int (*somefunc)(int n);
somefunc = □printf(“3 squared is %d\n”, (*somefunc)(3));
© 2010 Noah Mendelsohn
Be sure you understand why this works!
25
int square(int n) {
return n*n;}
int cube(int n) {
return n*n*n;}
intmain(int argc, char *argv[]){
int (*somefunc)(int n);
somefunc = □printf("3 squared is %d\n", (*somefunc)(3));
somefunc = &cube;printf("3 cubed is %d\n", (*somefunc)(3));
return 0;}
© 2010 Noah Mendelsohn
Be sure you understand why this works!
26
int square(int n) {
return n*n;}
int cube(int n) {
return n*n*n;}
intmain(int argc, char *argv[]){
int (*somefunc)(int n);
somefunc = □printf("3 squared is %d\n", (somefunc)(3));
somefunc = &cube;printf("3 cubed is %d\n", (*somefunc)(3));
return 0;}
The “*” is optional…
…modern versions of C provide it for you if you leave it out.
© 2010 Noah Mendelsohn
Using function pointers
You’ve seen a simple example
We often pass function pointers as arguments to other functions
This allows us to generalize over function
Examples from Hanson:– cmp and hash routines for tables
– apply() functions for mapping
27
© 2010 Noah Mendelsohn
One option: loops
29
/* NOT how Hanson usually does it */tab= Table_new(…):…add some data here..
for (i = 0, i < Table_length(arr) { upcase(Table_getnext(arr, NEXT));}
This is pseudo-code: some details just to show the idea.Hanson doesn’t do it this way anyway.
© 2010 Noah Mendelsohn
The functional (Hanson) way
30
void upcasefunction(void *key, void **value, void *cl) { …can uppercase **value here..}
tab= Table_new(…):…add some data here..Table_map(tab, upcasefunction, ??);
© 2010 Noah Mendelsohn
The functional (Hanson) way
31
void upcasefunction(void *key, void **value, void *cl) { …can uppercase **value here..}
tab= Table_new(…):…add some data here..Table_map(tab, upcasefunction, ??);
The map Table_map function loops calling upcasefunction repeatedly, once for each entry
Passing pointer to function
© 2010 Noah Mendelsohn
The closure: shared data for the mapping
32
void upcasefunction(void *key, void **value, void *cl) { …can uppercase **value here..}struct cl mycl {…shared data here..};
tab= Table_new(…):…add some data here..Table_map(tab, upcasefunction, &mycl);
The data in mycl is passed in from the caller……can be seen and updated in upcasefunction
© 2010 Noah Mendelsohn
Mapping vs loops
Map advantages:– Clean, easy to reason about
– Often less code to write
– Automatically gets the iteration right (e.g. length)
Disadvantages– Less flexible: what if we want to iterate in unusual order?
– Less obvious how to iterate multiple structures at once (there are ways)
– Getting closures right can be tricky
Mapping is a classic functional programming construct
33
© 2010 Noah Mendelsohn
Boxed and unboxed collections
The collections you’ve seen so far are boxed List_push(mylist, &data); /* list stores a pointer */
Problem: we want an array of 1 million characters– A character is 1 byte
– A pointer to a character is 8 bytes
– Even for bigger types, chasing pointers takes time, and allocating memory can be expensive
Unboxed collections: store the actual data, not a pointer!/* array of 1000000 chars */my_array = UArray_new(1000000, sizeof(char));/* set the char at offset 500 to ‘X’ */char *mychar = UArray_at(my_array, 500);*mychar = ‘X’;
35
© 2010 Noah Mendelsohn
Boxed and unboxed collections
The collections you’ve seen so far are boxed List_push(mylist, &data); /* list stores a pointer */
Problem: we want an array of 1 million characters– A character is 1 byte
– A pointer to a character is 8 bytes
– Even for bigger types, chasing pointers takes time, and allocating memory can be expensive
Unboxed collections: store the actual data, not a pointer!/* array of 1000000 chars */my_array = UArray_new(1000000, sizeof(char));/* set the char at offset 500 to ‘X’ */char *mychar = UArray_at(my_array, 500);*mychar = ‘X’;
36
Space for 1000000 characters is allocated in the Hanson array…we could use sizeof(struct SomeBigStruct) and that would work too!
© 2010 Noah Mendelsohn
Boxed and unboxed collections
The collections you’ve seen so far are boxed List_push(mylist, &data); /* list stores a pointer */
Problem: we want an array of 1 million characters– A character is 1 byte
– A pointer to a character is 8 bytes
– Even for bigger types, chasing pointers takes time, and allocating memory can be expensive
Unboxed collections: store the actual data, not a pointer!/* array of 1000000 chars */my_array = UArray_new(1000000, sizeof(char));/* set the char at offset 500 to ‘X’ */char *mychar = UArray_at(my_array, 500);*mychar = ‘X’;
37
Uarray_at returns a pointer directly into the data array that’s inside the Hanson implementation…that’s its contract.