Download Section Handout: The Incredibles - Programming Paradigms - 20 and more Exercises Programming Paradigms in PDF only on Docsity!
CS107 Handout 20 Spring 2008 April 28, 2008
Section Handout
Problem 1: The Incredibles (given on a midterm a few years ago)
Consider the following struct definition:
typedef struct { int violet; char *dash[2]; char superboy[4]; } superhero;
static superhero **pixar(superhero *syndrome); static superhero *theincredibles(short frozone, superhero elastigirl) { frozone += elastigirl.superboy[frozone]; ((superhero *)((superhero *) elastigirl.dash[0])->dash)->violet = 400; return *pixar(&elastigirl) + 10; }
Generate code for the entire theincredibles function. Be clear about what assembly
code corresponds to what line.
Problem 2: New Zoo Revue (given on a midterm several years ago)
You are to generate code for the following nonsense code. Don't be concerned about
optimizing your instructions or conserving registers. We don't ask that you draw the
activation records, but it can only help you get the correct answer if you do. Be clear
about what assembly code corresponds to each line of C.
struct human { int doug; short emmyjo[2]; };
struct other { char *freddie; struct human charlie; struct human *henrietta; };
static struct human **AskingQuestions(struct human *heroes); static struct human **BeingCalm(short *conformity, struct other gooddeeds) { conformity[conformity] = 0; gooddeeds += ((struct human *) (gooddeeds->henrietta[0].emmyjo))->doug; return AskingQuestions((struct human *) &gooddeeds); }
Generate code for the entire BeingCalm function.
line 1 line 2 line 3
(over)
The assembly code generated by the compilation of AskingQuestions is given below.
Provide a C-code implementation of AskingQuestions , given the constraint that all
local variables must be of type struct human and/or struct human * , and the only
typecast allowed is (struct other *).
SP = SP – 12;
R1 =.2 M[SP + 8];
M[SP + 4] = R1;
R2 = M[SP + 16];
BNE R2, 0, PC + 16;
R3 = M[SP];
R4 = R3 + 8;
M[SP] = R4;
RV = SP + 12;
SP = SP + 12;
RET;
Problem 2: New Zoo Revue (given on a midterm several years ago)
You are to generate code for the following nonsense code. Don't be concerned about
optimizing your instructions or conserving registers. We don't ask that you draw the
activation records, but it can only help you get the correct answer if you do. Be clear
about what assembly code corresponds to each line of C.
struct human { int doug; short emmyjo[2]; };
struct other { char *freddie; struct human charlie; struct human *henrietta; };
static struct human **AskingQuestions(struct human *heroes); static struct human **BeingCalm(short *conformity, struct other gooddeeds) { conformity[conformity] = 0; gooddeeds += ((struct human *) (gooddeeds->henrietta[0].emmyjo))->doug; return AskingQuestions((struct human *) &gooddeeds); }
- Generate code for the entire BeingCalm function.
// line 1 R1 = M[SP + 4]; // load conformity R2 =.2 M[R1]; // load *conformity R2 = R2 * 2; // manually compute offset from base address R3 = R1 + R2; // manually compute address M[R3] =.2 0; // and write a short 0 there..
// line 2 R1 = M[SP + 8]; // load gooddeeds, remember it R2 = M[R1 + 12]; // load henrietta[0] field R3 = M[R2 + 4]; // load doug field of human struct at emmyjo R4 = R3 * 16; // pointer math on struct other *: quantum is 16 bytes R5 = R1 + R4; // compute new value for gooddeeds M[SP + 8] = R5; // flush back to stack
// line 3 R1 = SP + 8; // remember &gooddeeds SP = SP – 4; // make space for param M[SP] = R1; CALL SP = SP + 4; // clean up space
RET; // leave RV alone, return
line 1 line 2 line 3
- The assembly code generated by the compilation of AskingQuestions is given below.
Provide a C-code implementation of AskingQuestions , given the constraint that all
local variables must be of type struct human and/or struct human * , and the only
typecast allowed is (struct other *).
SP = SP – 12;
R1 =.2 M[SP + 8];
M[SP + 4] = R1;
R2 = M[SP + 16];
BNE R2, 0, PC + 16; // potentially jumps to instruction loading RV R3 = M[SP]; R4 = R3 + 8; M[SP] = R4; RV = SP + 12; SP = SP + 12; RET;
Answer
static struct human **AskingQuestions(struct human *heroes) { struct human manners; struct human *remainingCalm; manners.doug = manners.emmyjo[0]; if (heroes == NULL) remainingCalm++; return &remainingCalm + 3; }
The struct human is 8 bytes, and pointers are 4 bytes. The SP = SP – 12 line makes
it clear that there are either two or three local variables—one pointer and one
direct structure, or three pointers. The .2 implies that a short resides at byte
offset 8, so that further requires that the first variable be of type struct human , and
therefore the second variable is a pointer.
char *boy; } couple;
vector generateAllCouples(vector *boys, vector *girls) { vector couples; VectorNew(&couples, sizeof(couple), CoupleFree, 0);
Problem 3: packPackets
You’re to write code that traverses a linked list of custom nodes and builds a
dynamically allocated array of bytes large enough to store the meaningful data held by
the linked list. Each node stores one or more packets of raw information, where each
packet is preceded by a two-byte short stating the length (in bytes) of the packet that
follows it. The end of the packet sequence is marked by a two-byte zero, and the four
bytes after that store the address of the next node in the list (or NULL , if the node is the
last in the list). The following node contains three packets of length 20, 10, and 30 bytes,
in that order. The number of bytes making up the entire node is 72.
You’re to write a function called packPackets that, given the address of the first node in
such a list, builds a dynamically allocated byte array where all packets in the linked list
are replicated verbatim, one after another, in the same order they appear in the list. So,
given the following list:
your packPackets function would return the address of the first byte of this dynamically
allocated figure:
Here are some constraints I’m imposing on your implementation.
- The address passed to packPackets is the address of the first short storing the
number of bytes making up the first packet in the first node.
4 0 NULL
- Each node alternates between shorts and packets. A two-byte zero marks the end
of the packet sequence in any given node. Every node ends with a four-byte
pointer identifying the location of the next node in the list.
- You should implement this function iteratively, and your algorithm should
accomplish everything in exactly one traversal. This requires you to call realloc
for each packet you encounter during your one traversal. [Tip: When NULL is
passed as the first argument to realloc , realloc just calls malloc on your behalf.]
This artificial constraint is being imposed so that I can test your understanding of
realloc.
- For simplicity, you may assume the list contains at least one node, and that each
node contains at least one packet.
- You needn’t worry about any alignment restrictions at all.
- You shouldn’t free or modify the original list in any way.
- Function: packPackets
- Builds a contiguous array version of the packet list structure according
- to the explanations provided on the prior page. The parameter passed
- is of type short *, because the first meaningful piece of information
- stored in the list is a two-byte short. The return value is of type
- void *, because the implementation has no type information about the
- packet data. */
void *packPackets(short *list) {
Solution 2: Matchmaking
vector generateAllCouples(vector *boys, vector *girls) { vector couples; VectorNew(&couples, sizeof(couple), CoupleFree, 0); int i, j; couple item;
for (int i = 0; i < VectorLength(boys); i++) { for (int j = 0; j < VectorLength(girls); j++) { item.boy = strdup(*(char *) VectorNth(boys, i)); item.girl = strdup((char **) VectorNth(girls, j)); VectorAppend(&couples, &item); } }
return couples; }
Solution 3: packPackets
- Function: packPackets
- Builds a contiguous array version of the packet list structure according
- to the explanations provided on the prior page. The parameter passed
- is of type short *, because the first meaningful piece of information
- stored in the list is a two-byte short. The return value is of type
- void *, because the implementation has no type information about the
- packet data. */
void *packPackets(short *list) { void *image = NULL; int imageSize = 0; while (list != NULL) { int packetSize = *list++; char *data = list; if (packetSize > 0) { image = realloc(image, imageSize + packetSize); memcpy((char *) image + imageSize, data, packetSize); imageSize += packetSize; list = (short *)(data + packetSize); } else { list = *(short **)data; // list = *(void **)data would work too } } return image; }
CS107 Handout 28
Spring 2008 May 12, 2008
Section Handout
RSS News Feed Madness
You're writing a concurrent C program to simulate the chaos at Terman the night that
Assignment 6: News Feed Aggregator comes due. All 10 TAs are there, ready to answer
questions through the night—in fact, the TAs love CS107 students so much that they all stay
until the very last student leaves. All 115 CS107 students come to Terman with what they
assume is just one bug. Each student waits for one of the 20 computers to become available
(and all computers are initially available), and once one becomes available, he logs in,
debugs for a while, and then searches for an available TA (all of whom are initially
available) to look at his code. The TA informs him of the number of bugs in his code. If
there are no bugs, the student rejoices, logs out, and leaves. If his code is still buggy, then
the student goes back to his computer and debugs a little more, and repeats the process. If
the TA ever reports that the number of bugs is 10 or more, the student gives up, logs out
and leaves without finishing.
Each of the 10 TAs sleeps until a student gets her attention. The student shows her his code,
she studies the code for a while, reports the number of bugs back the student, reads some of
her email, and then goes back to sleep. The very last student needs to wake up all the TAs
to tell them that they can all go home. Here is the starting main function for the Terman
Cluster simulation. You cannot change any of this code:
#define NUM_TAS 10 #define NUM_STUDENTS 115 #define NUM_MACHINES 20
void main(void) { int i; InitThreadPackage(false); for (i = 0; i < NUM_TAS; i++) ThreadNew("TA", TA, 1, i); for (i = 0; i < NUM_STUDENTS; i++) ThreadNew("Student", Student, 0); RunAllThreads(); }
// these simulation functions don't do anything, just "fake" static int Examine(void); // for TA, studies student code, returns bug count static void ReadEmail(void); // for TA, after helping student, before sleeping static void Debug(void) // for Student, to simulate debugging static void Rejoice(void) // for Student, after logging out
Assume the above helpers are already written and are thread safe, you can just call them
when you need to. Your job will be to write the TA and Student functions to properly
synchronize the different activities and efficiently share the common resources. During
section, you’ll all agree what global variables and Semaphores are needed, and then you’ll
continue to write the TA and Student functions.
if (numBugs == 0) Rejoice();
SemaphoreWait(studentsLeftLock); numStudentsLeft--; bool everyoneDone = (numStudentsLeft == 0); SemaphoreSignal(studentsLeftLock); // thought question: why can’t the two lines above be switched?
if (everyoneDone) { for (ta = 0; ta < NUM_TAS; ta++) { SemaphoreSignal(tas[ta].requested); } }
SemaphoreSignal(numMachinesAvailable); }
CS107 Handout 33
Spring 2008 May 19, 2008
Section Handout: All Things Scheme
Problem 1: Building Subsets of a Certain Size
Implement the k-subsets function, which accepts a set and a non-negative integer k and
constructs a list of all those subsets of the incoming set whose size just happens to equal k.
Your implementation should run in time that’s proportional to the number of subsets in the
final answer. In particular, you should not reuse the power-set implementation from lecture
and then filter on length, because that’s entirely too time-consuming when large sets are
paired with small values of k.
;; Function: k-subsets ;; ------------------- ;; k-subsets constructs a list of all those subsets ;; of the specified set whose size just happens to equal k. ;; ;; Examples: (k-subsets '(1 2 3 4) 2) - > ((1 2) (1 3) (1 4) (2 3) (2 4) (3 4)) ;; (k-subsets '(1 2 3 4 5 6) 1) - > ((1) (2) (3) (4) (5) (6)) ;; (k-subsets '(a b c d) 0) - > (()) ;; (k-subsets '(a b d d) 5) - > ()
(define (k-subsets set k) (
Problem 2: Up Down Permutations
a. An up-down list is a homogeneous list which alternates between local minima and local
maxima—that is, the second element is larger than the first and third, the third element is
smaller than the second and fourth, the fourth element is larger than the third and fifth,
and so on. Informally, the list zig-zags up and down as you march over all of its elements.
Using car-cdr recursion, implement the is-up-down routine, which returns #t if and only if
the specified list of atoms is an up-down list according to the specified predicate. List of
length 0 and 1 are automatically considered to be up-down lists.
;; Function: is-up-down? ;; --------------------- ;; Returns true if and only if the specified list is an up-down list ;; according to the specified predicate. ;; ;; Examples: (is-up-down? '() <) - > #t ;; (is-up-down? '(1) <) - > #t ;; (is-up-down? '(1 2 2 3) <) - > #f ;; (is-up-down? '(1 6 2 4 3 5) <) - > #f ;; (is-up-down? '(1 6 2 4 3 5) >) - > #f ;; down-up, but not up-down ;; (is-up-down? '(4 8 3 5 1 7 6 2) <) - > #f
(define (is-up-down? list comp) (
CS107 Handout 33S
Spring 2008 May 20, 2008
Section Solution: All Things Scheme
Problem 1: Building Subsets of a Certain Size
;; Function: k-subsets ;; ------------------- ;; k-subsets constructs a list of all those subsets ;; of the specified set whose size just happens to equal k. ;; ;; Examples: (k-subsets '(1 2 3 4) 2) -> ((1 2) (1 3) (1 4) (2 3) (2 4) (3 4)) ;; (k-subsets '(1 2 3 4 5 6) 1) -> ((1) (2) (3) (4) (5) (6)) ;; (k-subsets '(a b c d) 0) -> (()) ;; (k-subsets '(a b d d) 5) -> ()
(define (k-subsets set k) (cond ((eq? (length set) k) (list set)) ((zero? k) '(())) ((or (negative? k) (> k (length set))) '()) (else (let ((k-subsets-of-rest (k-subsets (cdr set) k)) (k-1-subsets-of-rest (k-subsets (cdr set) (- k 1)))) (append (map (lambda (subset) (cons (car set) subset)) k-1-subsets-of-rest) k-subsets-of-rest)))))
Problem 2: Up Down Permutations
a.
;; Function: is-up-down? ;; --------------------- ;; Returns true if and only if the specified list is an up-down list ;; according to the specified predicate. ;; ;; Examples: (is-up-down? '() <) -> #t ;; (is-up-down? '(1) <) -> #t ;; (is-up-down? '(1 2 2 3) <) -> #f ;; (is-up-down? '(1 6 2 4 3 5) <) -> #f ;; (is-up-down? '(1 6 2 4 3 5) >) -> #f ;; (is-up-down? '(4 8 3 5 1 7 6 2) <) -> #t
(define (is-up-down? ls comp) (or (null? ls) (null? (cdr ls)) (and (comp (car ls) (cadr ls)) (is-up-down? (cdr ls) (lambda (one two) (comp two one))))))
Notice the above version uses tradition car-cdr recursion, but constructs an anonymous
binary predicate out of the original by just switching the roles of the two arguments. (This
is how I got > and not >= from < .) Of course, the disadvantage of this implementation is
that layers of anonymous lambda s build up around the original for arbitrarily large lists.
The solution here is to employ some arm’s length recursion by ensuring that the first three
elements are low-high-low and then recurring on the cdr of the cdr using the original
predicate.
(define (is-up-down? ls comp) (or (null? ls) (null? (cdr ls)) (and (comp (car ls) (cadr ls)) (or (null? (cddr ls)) (and (comp (caddr ls) (cadr ls)) (is-up-down? (cddr ls) comp))))))
You could even use mutual recursion and define a sister is-down-up? function. In fact,
the second half of this problem does exactly that.
b.
;; Function: up-down-permute ;; -------------------------- ;; up-down-permute generates all those permutations of a list that ;; just happen to be up-down permutations. ;; ;; Examples: (remove 3 '(1 2 3 4 5 4 3 2 1)) -> (1 2 4 5 4 2 1) ;; (up-down-permute '()) -> (()) ;; (up-down-permute '(1)) -> ((1)) ;; (up-down-permute '(1 2)) -> ((1 2)) ;; (up-down-permute '(1 2 3)) -> ((1 3 2) (2 3 1)) ;; (up-down-permute '(1 2 3 4 5)) -> ;; ((1 3 2 5 4) (1 4 2 5 3) (1 4 3 5 2) (1 5 2 4 3) (1 5 3 4 2) ;; (2 3 1 5 4) (2 4 1 5 3) (2 4 3 5 1) (2 5 1 4 3) (2 5 3 4 1) ;; (3 4 1 5 2) (3 4 2 5 1) (3 5 1 4 2) (3 5 2 4 1) ;; (4 5 1 3 2) (4 5 2 3 1))
(define (construct-permute-generator comp inverted-permute ls) (lambda (number) (apply append (map (lambda (permutation) (if (comp number (car permutation)) (list (cons number permutation)) '())) (inverted-permute (remove ls number))))))
(define (up-down-permute ls) (if (<= (length ls) 1) (list ls) (apply append (map (construct-permute-generator < down-up-permute ls) ls))))
(define (down-up-permute ls) (if (<= (length ls) 1) (list ls) (apply append (map (construct-permute-generator > up-down-permute ls) ls))))
Problem 2: Maximizing Points
You’re given an unlimited number of pebbles to distribute across an N x N game board
(N drawn from [3, 15]), where each square on the board contains some positive point
value between 10 and 99, inclusive. A 6 x 6 board might look like this:
The player distributes pebbles across the board so that:
- At most one pebble resides in any given square.
- No two pebbles are placed on adjacent squares. Two squares are considered
adjacent if they are horizontal, vertical, or even diagonal neighbors. There’s no
board wrap, so 44 and 61 aren’t neighbors. Neither are 33 and 75.
The goal is to maximize the number of points claimed by your placement of pebbles.
Write a program that reads in a sequence of boards from standard input and posts the
maximum number of points attainable by an optimal pebble placement for each. Each
board is expressed as a series of lines, where each line is a space-delimited series of
numbers. A blank line marks the end of each board (including the last one.)
The better solution to this problem uses memoization to reduce the running time of the
solution from something very exponential in running time to something noticeably less
exponential (though still exponential.) Don’t worry too much about the file reading
portion of the solution—focus instead on the ideal distribution of pebbles and the
Python code that helps to discover it. (See the last page of this handout for a list of all
dictionary and mutable sequence methods.)
So, if the following were fed to standard input:
then your program would print the maximum number of points one can get by
optimally distributing pebbles while respecting the two rules, which would be this: