on Abstract Data Types (pptx)

Download Report

Transcript on Abstract Data Types (pptx)

COMP 40: Machine Structure
and
Assembly Language Programming (Fall 2016)
More on Abstract Data Types
Noah Mendelsohn and Mark Sheldon
Tufts University
Email: [email protected]
Web: http://www.cs.tufts.edu/~noah
Last time we covered
 How data and pointers are stored in the computer’s memory
 Abstraction, modularity, reuse, information hiding
 Generalizing over values and types
 The special role of void * and incomplete structures
Today
 Generalizing over computation
 Function pointers
 Iteration and mapping in a functional style
 Boxed and unboxed data structures
2
© 2010 Noah Mendelsohn
Quick Review
© 2010 Noah Mendelsohn
Software structures model real world objects and concepts
 Integers
 Students
 Bank statements
 Photographic images
The course materials refer to these
as being in the “World of Ideas”
 Sound recordings
 Etc.
We build data structures that model them
5
© 2010 Noah Mendelsohn
E.g. methods in Hanson table.h
Interfaces
Interface
Client
Implementation
E.g. your fgroups program
E.g. implementation of
Hanson Table_T
7
© 2010 Noah Mendelsohn
Interfaces
The implementation chooses a
representation … NOT visible to
the client
8
Interface
Client
Implementation
© 2010 Noah Mendelsohn
E.g. methods in Hanson table.h
Interfaces
Q. What can Table_new return?
Client
Interface
My_table = Table_new(…
… …)
Implementation
Last time we showed you two ways of doing this
E.g. your fgroups program
E.g. implementation of
Hanson Table_T
9
© 2010 Noah Mendelsohn
Hanson does this with incomplete structs
struct Table_T *table_ptr;
struct Table_t *table_ptr;
Client has
incomplete
declaration of the
struct
Interface
Client
Struct Table_t {
…data declartions…
Implementation
}
my_table = Table_new(…
Table_put(my_table, …)
13
… …)
© 2010 Noah Mendelsohn
By the way, this is exactly what languages like C++ do
class Myclass
{
int some_method(int a) {…};
}
Myclass my_object = new Myclass();
my_object -> some_method(5);
Under the covers, C++ implements this as:
some_method(my_object, 5);
14
© 2010 Noah Mendelsohn
Generalization
© 2010 Noah Mendelsohn
You’ve already seen some generalization
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?
16
© 2010 Noah Mendelsohn
We need to generalize over types
List_of_students_new(…);
List_of_cars_new(…);
List_of_bank_stmts_new(…);
List_new(…);
We want this!
We don’t want this
Can we generalize over the type of information?
How do we declare the input to List_push()?
(after all, its type could be anything)
17
© 2010 Noah Mendelsohn
How do we declare List_push?
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);
List_push(mylist, &mycar);
18
/* create empty list */
/* put my car on the list */
© 2010 Noah Mendelsohn
Void * allows us to generalize over types
The list will remember a pointer to
anything.
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);
List_push(mylist, &mycar);
19
Any pointer can be passed
to a void * parameter
/* create empty list */
/* put my car on the list */
© 2010 Noah Mendelsohn
Void * allows us to generalize over types
void List_push(List_T list, void *x);
This generalization is known as
universal
polymorphism
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);
List_push(mylist, &mycar);
/* create empty list */
/* put my car on the list */
List_pop(mylist, &retrieved_car_p);
20
/* get back mycar */
© 2010 Noah Mendelsohn
Void * allows us to generalize over types
void List_push(List_T list, void *x);
Hanson’s
declaration for List_push
IMPORTANT:
Retrieved_car_p is already a pointer.
Why do we have to pass the address
struct Car {char *brand; int weight};
of retrieved_car_p?
typedef struct Car Car;
Car mycar = {“ford”, 2000};
Car *retrieved_car_p;
mylist = List_list(NULL);
List_push(mylist, &mycar);
/* create empty list */
/* put my car on the list */
List_pop(mylist, &retrieved_car_p);
21
/* get back mycar */
© 2010 Noah Mendelsohn
Aside: how can a function modify
arguments passed to it?
© 2010 Noah Mendelsohn
Can a function change its argument?
void doubleit(int n)
{
n = n * 2;
return;
}
void doubleit(int *np)
{
*np = *np * 2;
return;
}
int x = 3;
doubleit(x);
printf(“x = %d\n”, x);
int x = 3;
doubleit(&x);
printf(“x = %d\n”, x);
This won’t work
Prints:
23
x = 3
This will work
Prints:
x = 6
© 2010 Noah Mendelsohn
What if the variable to be changed is already a pointer?
void getstring(char **stringpp)
{
*stringpp = ...;
return;
}
char *myString;
getstring(&myString);
Does this remind you of
24
readaline?
© 2010 Noah Mendelsohn
Generalizing Over
Computation
© 2010 Noah Mendelsohn
Three ways of generalizing
 Parameters generalize values
 To generalize over types (of pointers) in C, we
use void pointers (other languages have more
direct ways of doing this)
26
© 2010 Noah Mendelsohn
Three ways of generalizing
 Parameters generalize values
 To generalize over types (of pointers) in C, we
use void pointers (other languages have more
direct ways of doing this)
 To generalize over computations, use function
pointers
27
© 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.
28
© 2010 Noah Mendelsohn
Function pointers in C
 We know the code is living in memory
 Can we take the address of code? Yes!
Syntax
int square(int n)
{
return n * n;
}
int (*somefunc)(int n);
somefunc = □
printf(“3 squared is %d\n”, (*somefunc)(3));
29
© 2010 Noah Mendelsohn
Function pointers in C
 We know the code is living in memory
 Can we take the address of code? Yes!
Syntax
int square(int n)
{
return n * n;
}
int (*somefunc)(int n);
somefunc = □
printf(“3 squared is %d\n”, (*somefunc)(3));
30
© 2010 Noah Mendelsohn
Function pointers in C
 We know the code is living in memory
 Can we take the address of code? Yes!
Syntax
int square(int n)
{
return n * n;
}
int (*somefunc)(int n);
somefunc = □
printf(“3 squared is %d\n”, (*somefunc)(3));
31
© 2010 Noah Mendelsohn
Function pointers in C
 We know the code is living in memory
 Can we take the address of code? Yes!
Syntax
int square(int n)
{
return n * n;
}
int (*somefunc)(int n);
somefunc = □
printf(“3 squared is %d\n”, (*somefunc)(3));
32
© 2010 Noah Mendelsohn
Be sure you understand why this works!
int square(int n)
{
return n * n ;
}
int cube(int n)
{
return n * n * n ;
}
int main(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 EXIT_SUCCESS;
}
33
© 2010 Noah Mendelsohn
Be sure you understand why this works!
int square(int n)
{
return n * n;
}
int cube(int n)
{
return n * n * n;
}
int main(int argc, char *argv[])
{
int (*somefunc)(int n);
The “*” is optional…
…modern versions of C provide it for
you if you leave it out.
somefunc = □
printf("3 squared is %d\n", (somefunc)(3));
somefunc = &cube;
printf("3 cubed is %d\n", (*somefunc)(3));
return EXIT_SUCCESS;
}
34
© 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 functionals/computations
 Examples from Hanson:
– cmp and hash routines for tables
– apply() functions for mapping
35
© 2010 Noah Mendelsohn
Computing on Collections
© 2010 Noah Mendelsohn
One option: loops
/* NOT how Hanson usually does it */
tab = Table_new(…):
…add some data here..
for (i = 0; i < Table_length(tab); ++i) {
upcase(Table_getnext(tab, NEXT));
}
This is pseudo-code: some details just to show the idea.
Hanson doesn’t do it this way anyway.
37
© 2010 Noah Mendelsohn
The functional (Hanson) way
void upcasefunction(void *key, void **value,
void *cl) {
…can uppercase **value here..
}
tab= Table_new(…):
…add some data here..
Table_map(tab, upcasefunction, ??);
38
© 2010 Noah Mendelsohn
The functional (Hanson) way
void upcasefunction(void *key, void **value,
void *cl) {
…can uppercase **value here..
}
Passing pointer to
function
tab= Table_new(…):
…add some data here..
Table_map(tab, upcasefunction, ??);
The map Table_map function loops calling upcasefunction
repeatedly, once for each entry
39
© 2010 Noah Mendelsohn
The closure: shared data for the mapping
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
40
© 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
41
© 2010 Noah Mendelsohn
Unboxed Data
Generalizing to Collections of
Objects that Aren’t Pointers
© 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, but a pointer to a character is 8 bytes
– Even for bigger types, chasing pointers takes time, and allocating memory can
be expensive
– Sometimes, we get arrays from or provide arrays to other code
 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’;
43
© 2010 Noah Mendelsohn
Boxed and unboxed collections
Space for 1000000 characters is
 allocated
The collections
you’ve
seen so far are boxed
in the Hanson
uarray…we
List_push(mylist,
&data); /* list stores a pointer */
could
use sizeof(struct
SomeBigStruct) and that would
 work
Problem:
too! 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’;
44
© 2010 Noah Mendelsohn
Boxed and unboxed collections
 The collections you’ve seen so far are boxed
List_push(mylist, &data); /* list stores a pointer */

Uarray_at returns a pointer directly
intoarray
the data
that’s
inside the
Problem: we want an
of 1array
million
characters
Hanson implementation…that’s its
– A character is 1 byte contract.
– 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’;
45
© 2010 Noah Mendelsohn
Wrapap
© 2010 Noah Mendelsohn
Summary
 Generalization over computation with function pointers
– E.g. comparison routine for polymorphic sorting
– Apply function for mapping…see below
 Aside on changing caller data by passing pointers
 Mapping: a well-structured functional way to:
– Iterate over a data structure
– Apply some specified function to each element
 Closures:
– Maintain context/state while calling out to functions
– Hanson-style: build it by hand
– Models what many modern programming languages do for you
 Boxed vs. unboxed collections
47
© 2010 Noah Mendelsohn