Note: In using this to help prepare for the midterm in any given semester, please keep in mind that the material covered in your semester might differ somewhat from that covered when this exam was given.

 

Your name __________________________

Harvey Mudd College

CS 60 Mid-Term Exam

Spring semester, 1996

Six Problems

Open book and notes

The exam is planned for 1 hour, 15 mins., ending at 4:00 p.m. You may take up to 15 minutes longer if the room is not being used for something else.

Please provide answers to the problem groups directly on these pages. It is suggested that you look over the exam first to get an idea of how to apportion your time. Work so as to maximize total points; do not get stuck on one problem at the expense of others.

During the exam, you may refer to any of your own reference materials, but consulting with others or using their materials is forbidden.

When code is requested, exhibiting the correct idea is more important than syntactic precision. However, keep your definitions clean and readable. Use the clearest program structure possible.

The answers to these problems need not be exceptionally long. Please think through your solution before you plunge into a long exposition that might not address the main point of the question.


1. [10 points]

An unfortunate nim player has piles of the following sizes: 63, 31, and 1. What move should he make to reduce the nim sum to 0? Explain why this move is correct and how you found your answer.


2. [15 points]

State as clearly as possible, and in your own words, the differences between the Polya functions cons, list (of 2 arguments only), and appnd. Give examples to help clarify.


3. [10 points]

A tree can be enlarged from the leaves downward by viewing the tree as a list and applying the map function to that list along with an appropriate function argument. For example, if the tree were [1, 2, 3], we could replicate its leaves side-by-side to get a new tree [[1, 1], [2, 2], [3, 3]].

a. Give a function which could be given as the first argument of map to achieve the transformation from [1, 2, 3] to [[1, 1], [2, 2], [3, 3]].

b. Using recursion, in conjunction with map, present rex rules for a single function which can replicate the leaves of an arbitrary tree. For example, replicating leaves in

[[1, 2], 3, [[4, 5], 6]]

should yield

[[[1, 1], [2, 2]], [3, 3], [[[4, 4], [5, 5]], [6, 6]]].


4. [15 points]

Consider the set S of functions with domain and range being subsets of the natural numbers. For example, the factorial function fac is such a function.

Construct rex definitions for a Curried higher-order function inverse which will produce the inverse of an arbitrary function in S. The inverse of a function is here defined to be another function which, given a result of the original function, produces the smallest argument of the original function which would have yielded that result. For example, since we have

fac(0) = 1, fac(1) = 1, fac(2) = 2, fac(3) = 6, fac(4) = 24, fac(5) = 120, ...

we would have

inverse(fac)(1) = 0, inverse(fac)(2) = 2, inverse(fac)(6) = 3, inverse(fac)(24) = 4,

inverse(fac)(120) = 5, ...

Note that when there might have been more than one possible answer, as is the case with inverse(fac)(1), we used the convention that the smallest such answer is returned by inverse. So inverse(fac)(1) is 0 rather than 1. Also, when there is no possible answer, inverse will diverge (give no answer). So inverse(fac)(3), for example, will diverge.

The inner workings of inverse will have to try evaluating the argument function for successive values 0, 1, 2, ... until one is found which works. So this would be the rex equivalent of a while loop. Also be reminded that your definition must work for an arbitrary function argument, not just fac.


5. [10 points]

The cells of a doubly-linked list are declared as follows:

struct Cell 
{ 
T data; 
Cell* next; 
Cell* previous; 
}; 

Suppose that p points to such a cell inside a list and that this cell is not the first, last, nor second from last cell in the list.

a. Assuming that a and b are pointers to cells, write C++ statements which set a to point to the cell after the target of p and b to point to the cell before the target of p.

b. Using those values of p, a, and b, write C++ code which will transpose the target of p and the cell immediately following it. In other words, if the list contains (1 2 3 4) and p points to the cell containing 2, then after running your code, the list will contain (1 3 2 4).

Demonstrate that your code is correct by means of a single box-and-pointer diagram in which arrows are used to represent pointers before and after the change. Please use the following convention so that I can easily read your diagram: solid lines refer to pointers before the transposition; dashed lines refer to pointers after the transposition. Label the dashed lines with circled numbers keyed to C++ statements which gave rise to those pointers.


6. [40 points]

By using prefix (or “Polish”) notation instead of infix for arithmetic, we can get an unambiguous grammar without using parentheses. Probably the simplest example is the following grammar for mod-2 arithmetic:

S -> `0'

S -> `1'

S -> `*' S S

S -> `+' S S

a. [2 of 40 points] Give examples of strings over the alphabet {`0', `1', `+', `*'} of length 15 or more, one of which is in the language generated by the grammar and the other of which is not.

b. [3 of 40 points] Give the derivation tree for your answer to part a which is in the language.

c. [30 of 40 points] One set of evaluation rules for this language is based on + x y being, in infix notation, (x + y) % 2 and * x y being (x * y) % 2, where % is the usual C++ remainder or modulus function. That is,

the following each evaluate to 0:

* 0 0     * 0 1      * 1 0      + 0 0      + 1 1 

while the following each evaluate to 1:

+ 0 1     + 1 0     * 1 1 

For example,

+ 0 * + 0 1 * 1 1 

evaluates to 1, according to a series of rewrites (where the rewritten string is underlined) as follows:

+ 0 * + 0 1 * 1 1 ==> + 0 * 1 * 1 1 
                      + 0 * 1 * 1 1 ==> + 0 * 1 1 
                                        + 0 * 1 1 ==> + 0 1 
                                                      + 0 1 ==> 1  

Using grammatical principles, construct a combined parser and evaluator which will evaluate expressions in the language generated by this grammar. You may assume there is no whitespace (whitespace in the above examples being just for readability) and that the input stream is terminated by any character other than 0, 1, +, and *. If the input string prior to termination is in the language, the program should give the corresponding value. If not, the program should announce that there is a syntax error.

This code can be kept simple, so please try!