This preview shows page 1. Sign up to view the full content.
Unformatted text preview: CS 61A Spring 2004 Midterm #1 solutions Note: If your individual score is I points (out of 40) and your group score
is G points (out of 13), your overall score will be computed as (max I (+ (* 0.8917 I) (* 0.3333 G))) The theory behind this formula is that 27 of your 40 individual points are
not matched by anything in the group exam, and for the 13 points that are
matched, we want to weight the exams as 2/3 individual, 1/3 group. So that
gives (+ (* 27/40 I) (* 2/3 (* 13/40 I)) (* 1/3 G)), except that if your
individual score is better than the combined score by this formula, we'll
keep your individual score. But this correction is done at the end of the
semester in the final gradereporting program; GLOOKUP isn't smart enough to
report anything but a fixed weighting of points. 1. What will Scheme print? Scoring: One point each, all or nothing, except that we deducted at most one
point for leaving out parentheses from any number of otherwise correct
answers. We didn't take off points for quotation marks in front of othherwise
correct answers, but we will next time  *you* type quotation marks in front
of sentences you want Scheme to treat as data rather than as procedure calls,
but the quotation marks are not part of the values, and Scheme doesn't
print them. 1) > (se 'a '(a)) (A A) SENTENCE accepts words and/or sentences as arguments, and always returns a sentence. The most common wrong answer was (A (A)), which is not a sentence. That would be the answer if the procedure were LIST rather than SE. 2) > (word 'a '(a)) ERROR The domain of WORD is words, not sentences. It doesn't make sense to make a word by combining sentences! 3) > ((lambda (x) (bf (bl x))) '(abcd efgh)) () (bl '(abcd efgh)) ==> (abcd) (bf '(abcd)) ==> () The key point here is that there's a difference between the word ABCD and the onewordlong sentence (ABCD). BUTFIRST of a word is all but the first letter. BUTFIRST of a sentence is all but the first *word*, and that remains true even if the sentence has only one word. Removing the first word of (ABCD) gives the empty sentence. The most common wrong answer, BCD, comes from taking (butfirst 'abcd) instead of (butfirst '(abcd)). 4) > (every (lambda (x) (bf (bl x))) '(abcd efgh)) (BC FG) Most people got this one. In this case we're taking the butlast and butfirst of words, not of sentences. 5) > (lambda (abcd) (bf (bl abcd))) PROCEDURE Most people got this one. LAMBDA creates a procedure, and nothing is invoking the procedure. The ABCD here is a formal parameter, not an actual argument, so BC would be wrong. 6) > (se (first 'goodbye) (first '(hello))) (G HELLO) Like the third example, this one is about the difference between a word and a onewordlong sentence: (first 'goodbye) ==> G (first '(hello)) ==> HELLO The most common mistake was (G H), ignoring that difference. 7) > (cond (3 4) (5 6) (else 7)) 4 Every value other than #F is considered true by IF and COND. In particular, 3 is true, so the first COND clause is satisfied. The most common wrong answer was ERROR, presumably because these clauses look unusual. You're accustomed to nested parentheses in a COND clause: ((equal? ....) (....)) But the two expressions inside the clause don't have to be procedure calls. 2. Makeclosestvalue. The recursive use of makeclosestvalue is a little tricky. The simplest
solution is to avoid it by using a recursive helper function. There are
several ways to organize the helper. The most elegant uses a recursive
process: (define (makeclosestvalue nums) (define (closestvalue num nums) (if (empty? (butfirst nums)) (first nums) (closer num (first nums) (closestvalue num (butfirst nums))))) (lambda (num) (closestvalue num nums))) Here the recursive call to closestvalue provides one of the arguments
to closer. It's also not too bad to use an iterativeprocess helper
procedure that takes an extra resultsofar argument: (define (makeclosestvalue nums) (define (closestvalue num sofar rest) (if (empty? rest) sofar (closestvalue num (closer num sofar (first rest)) (butfirst rest)))) (lambda (num) (closestvalue num (first nums) (butfirst nums)))) Somewhat less elegant is a solution that uses SENTENCE to stick the
resultsofar at the front of a smaller sentence: (define (makeclosestvalue nums) (define (closestvalue num nums) (if (empty? (butfirst nums)) (first nums) (closestvalue num (sentence (closer num (first nums) (first (bf nums))) (bf (bf nums)))))) (lambda (num) (closestvalue num nums))) If you do want to invoke makeclosestvalue recursively, it's important to
remember that it returns a procedure, not a number or a sentence! So you have
to invoke that procedure, not just try to put it in a sentence. Any of the
solutions above can be turned into one that calls makeclosestvalue
recursively; I'll just show the equivalent to the first one: (define (makeclosestvalue nums) (lambda (num) (if (empty? (butfirst nums)) (first nums) (closer num (first nums) ((makeclosestvalue (butfirst nums)) num) ))))  The underlined subexpression is (XXX num), where XXX is the procedure
returned by the recursive call to makeclosestvalue. Yet another technique is available to those who read ahead to the higher
order function ACCUMULATE defined for lists on page 116: (define (makeclosestvalue nums) (lambda (num) (accumulate (lambda (x y) (closer num x y)) (first nums) (butfirst nums)))) There were two assumptions about the domain that some students made, which
were true in the examples we showed but were not justified by the problem
statement: One is that NUMS has length at least two, and the other is that
NUMS is sorted. Scoring: 7 correct. 6 trivial errors. For example:  assumes count >= 2;  returns sentence containing a number, instead of a number. 5 has the idea. For example:  assumes NUMS is ordered;  never examines (last nums). 3 has an idea. For example:  function returned by recursive call to makeclosestvalue isn't invoked. 0 other. For example:  no recursion at all;  tries to use EVERY. 3. Values. The key insight is that this is a KEEPlike recursion, even though there
is no explicit sentence of numbers as an argument. So the solution is a
cross between the SUM procedure from Chapter 1 and KEEP: (define (values pred from to) (cond ((> from to) '()) ((pred from) (se from (values pred (+ from 1) to))) (else (values pred (+ from 1) to)))) An equivalent solution with a little less typing cleverly uses the fact
that SENTENCE just ignores an empty sentence given to it as an argument: (define (values pred from to) (if (> from to) '() (se (if (pred from) from '()) (values pred (+ from 1) to)))) It's possible to use (= from to) as the base case test instead of
(> from to), but trickier. You have to remember that the range of VALUES
is sentences, not numbers, so it has to return (SE FROM), not just FROM. Some students tried to use KEEP itself. This would be reasonable if we'd
allowed the use of helper procedures: (define (values pred from to) ;; would be okay if not disallowed (define (range from to) (if (> from to) '() (sentence from (range (+ from 1) to)))) (keep pred (range from to))) Since we didn't allow helpers, some people crammed this all into one
procedure, along these lines: (define (values pred from to) ;; really ugly! (if (> from to) '() (keep pred (se from (values pred (+ from 1) to))))) This works, but it's not a satisfactory solution, because it calls KEEP not
just once, which is the way HOFs are supposed to be used, but N times, once in
each recursive call to VALUES. So there are Theta(N^2) calls to PRED! In
this course you don't have to bend over backwards to be efficient, but you
shouldn't bend over backwards to be inefficient either! Why use KEEP at all?
Even if you could make only one call to KEEP, you still have to construct two
sentences that way, the sentence of all numbers between FROM and TO, and the
sentence of those numbers that satisfy the predicate. Scoring: 7 correct. 6 trivial error. For example:  returns number instead of sentence in base case. 5 has the idea. For example:  uses KEEP;  puts (PRED FROM) into the sentence instead of FROM. 3 has an idea. For example:  uses helper procedure. 0 other. 4. Order of growth. The call to COUNT is Theta(N), and the call to Factorial is also Theta(N).
Each of those calls happens only once, so the result is still Theta(N). [It's
fine to say Theta(2N) but unnecessary.] Strictly speaking, there is no "N" in FACTCOUNT, so the truly correct answer
is Theta(COUNT SENT), and indeed some students said that. For our purposes in
61A, all we care about is that you recognize this as lineartime, not
quadratictime. But you should know that in 61B and later courses, they'll
probably insist on Theta(COUNT SENT) here; Theta(N) would be marked wrong. The only common wrong answer was Theta(N^2), presumably because you multiplied
the order for COUNT by the order for FACTORIAL. That would have been right if,
for example, COUNT were invoked *inside* FACTORIAL, like this: (define (funnyfact sent) (if (empty? sent) 1 (* (count sent) (funnyfact (butfirst sent))))) But in the actual problem, FACTCOUNT, which is not itself recursive,
invokes COUNT and FACTORIAL exactly once each. Scoring: 2 points, all or nothing. 5. Normal vs. applicative. In Applicative order, which is what Scheme actually uses, the way to
evaluate a procedure call is first to evaluate subexpressions, and then
substitute the resulting values into the procedure body: 1. Evaluate subexpressions: (+ 1 2) ==> 3 (+ 3 4) ==> 7 (+ 5 6) ==> 11 2. Substitute values into body: (if (= 3 3) 7 11) This returns the value 7. In Normal order, the way to evaluate a procedure call is to substitute
the argument *expressions* into the body, then evaluate the result: 1. Substitute: (if (= (+ 1 2) 3) (+ 3 4) (+ 5 6)) 2. Evaluate the IF expression. IF is a special form, so we don't evaluate all its subexpressions at once. We start by evaluating the first one: (= (+ 1 2) 3) ==> (= 3 3) ==> #T Since the result is true, we evaluate the second argument expression, but not the third: (+ 3 4) ==> 7 Reviewing these two processes, we see that (+ 1 2) was evaluated in both
Normal and Applicative order, because IF needs its value to know which
of the other expressions to evaluate. But (+ 5 6) is evaluated only in
applicative order. So the correct answers are: (a) Both normal and applicative order. (b) Applicative order. A common mistake was to think that the question was asking whether,
in a standard (applicativeorder) Scheme, just the subexpressions
(+ 1 2) and (+ 5 6) would be evaluated in normal or applicative order.
But that wouldn't make sense, for at least two reasons: (1) The "both"
answer couldn't possibly be right if that were the question; (2) when
primitive procedures (such as +) are invoked, the argument expressions
have to be evaluated in any case. The normal/applicative distinction
is about invocations of lambdadefined procedures. Scoring: 2 points each, all or nothing. 6. Iterative vs. recursive process. The moral of this problem is supposed to be that iterativeprocess code
is often really hard to read and understand. (a) The given procedure generates an ITERATIVE process. The recursive
helper procedure HELP invokes itself only as the entire action part of
two COND clauses. These are tail calls, just as a recursive call in the
second or third argument to IF is a tail call. Scoring: 1 point, all or nothing. (b) Here's a trace: (seq 4)
(help 4 1 '())
(help 4 2 '(1))
(help 4 3 '(1 2))
(help 4 4 '(1 2 3))
(help 4 5 '(1 2 3 4))
(help 3 1 '(1 2 3 4))
(help 3 2 '(1 2 3 4 1))
(help 3 3 '(1 2 3 4 1 2))
(help 3 4 '(1 2 3 4 1 2 3))
(help 2 1 '(1 2 3 4 1 2 3))
(help 2 2 '(1 2 3 4 1 2 3 1))
(help 2 3 '(1 2 3 4 1 2 3 1 2))
(help 1 1 '(1 2 3 4 1 2 3 1 2))
(help 1 2 '(1 2 3 4 1 2 3 1 2 1))
(help 0 1 '(1 2 3 4 1 2 3 1 2 1)) So the value is (1 2 3 4 1 2 3 1 2 1). Some wrong answers just had the order wrong, e.g., (4 3 2 1 3 2 1 2 1 1);
a common really wrong answer was (1 2 3 4). Scoring: 1 point, all or nothing. (c) The most straightforward way to solve this problem is to look at
the answer to (b), ignore the complicated procedure, and rewrite it from
scratch. The desired value is a sequence of sequences, so it makes sense
to use a helper that generates a sequence such as (1 2 3): (define (upto k) (if (= k 0) '() (se (upto ( k 1)) k))) (define (seq n) (if (= n 0) '() (se (upto n) (seq ( n 1))))) It's also possible to start with the given procedure, eliminate the
extra resultsofar argument SENT, and change the actions in the COND
clauses. But it's important to remember the reversing effect of
iterativeprocess procedures, so (SE SENT K) becomes (SE K <recur>),
not (SE <recur> K): (define (seq n) (define (help n k) (cond ((= n 0) '()) ((> k n) (help ( n 1) 1)) (else (se k (help n (+ k 1)))))) (help n 1)) It's also possible to meet the literal requirements of the question, but
without gaining the recursiveprocess benefit of simplifying the code,
by just adding a redundant call to SE in the ELSE clause: (define (seq n) (define (help n k sent) (cond ((= n 0) sent) ((> k n) (help ( n 1) 1 sent)) (else (SE (help n (+ k 1) (se sent k)))))) (help n 1 '())) We didn't give that full credit because we thought that it missed the
point of the question, even though it happens to work. Scoring: 4 correct. 3 trivial error, e.g., wrong base case; redundant SE as discussed above; otherwise 2point solution that correctly generates the incorrect but almostcorrect solution to part (b). 2 has the idea: recursive process, but notquiteright sequence. 1 iterative process, correct or almostcorrect sequence. 0 other, e.g., generates (1 2 3 4). 7. Debugging. The point of this question is to test debugging skills, which include: 1. Careful observation of program behavior. 2. Inference of *likely* (not just possible) code errors consistent with that behavior. (a) Which arguments produce incorrect behavior? Almost everyone got this: The program gives wrong answers for arguments for
which subtraction of a smaller lettervalue from a larger one is needed. Some people restricted the incorrectanswer cases to the ones shown in our
examples: IV, CD, and XC. Other people decided that the error in the last
example was entirely due to one of the subtractions (most often, CD but not
XC). They either listed specific cases or attempted to generalize from
them, giving rise to answers like the following: It fails only if the smallthenlarge letters differ by only one position in the MDCLXVI hierarchy. It fails for the first smallthenlarge pair in the argument. We accepted these, since part (a) is just about observation, not about
inference. We rejected similarlooking answers that were inconsistent
with the data, such as: It doesn't fail if the smallthenlarge pair is at the end of the argument word. [Contradicts the MMIV case!] It fails only if there are two smallthenlarge pairs. [Ditto!] It fails only for the pair IV. [Contradicts MCDXCII!] Some people listed arguments that are not in the program's intended domain,
such as nonletters or letters other than MDCLXVI. This was a
misunderstanding of the question; we didn't ask for arguments for which the
program can't work, but rather for arguments for which the program's behavior
doesn't meet its specification  ones that give rise to wrong answers when a
right answer is possible. (b) For those arguments, what wrong answer is produced? The right answer is that for each smallthenlarge pair, the second (larger)
letter is counted twice. In the first example, MMIV, the right answer would be 2004; the given wrong
answer, 2009, is 5 more than it should be  the value of V. The second and third examples work correctly. In the fourth example, MCDXCII, the correct answer is 1492. The given wrong
answer, 2092, is too great by 600, which is 500+100  the values of D and C. It's probably because the last two digits of the given answer are correct that
a lot of students thought, in part (a), that the error is entirely due to the
CD pair. But it's hard to think of any way that particular wrong answer would
come up, unless the values for every possible smallthenlarge pair are built
into the program explicitly. A few students said that the value for the letter one higher than the
larger letter was used, e.g., in the pair IV, use the value for X instead
of the value for V. This works in that case because V and X differ by a
factor of two, but it fails in a case like XC, because next up from C is D,
which is a factor of five larger. Some students said "the result is bigger than it should be" without saying
how much bigger. We gave this part credit. (c) What is the probable error in the program code? The second (largervalued) letter of the pair is counted twice. Most likely,
this means that the program is examining it twice. Here is a correct
procedure: (define (arabic num) (cond ((empty? (bf num)) (lettervalue num)) ((< (lettervalue (first num)) (lettervalue (first (bf num)))) (+ ( (lettervalue (first (bf num))) (lettervalue (first num))) (arabic (BF (BF NUM))))) (else (+ (lettervalue (first num)) (arabic (bf num)))))) In the second COND clause, dealing with smallbeforelarge pairs, the
correct program does the subtraction, then *skips over both letters*
using the (BF (BF NUM)) expression capitalized above. The most likely
error is that one call to butfirst has been omitted. It wasn't necessary to show actual code; "skips over one letter instead
of two" was plenty. Some students suggested that the value of the larger letter was multiplied by
two in the procedure, like this: (+ ( (* 2 (LETTERVALUE (FIRST (BF NUM)))) (lettervalue (first num))) (arabic (bf (bf num))))) But why on earth would anyone make this mistake? It's not something you'd be
likely to do by accident. We gave no credit for this, as an answer to (c),
although it's an acceptable answer to (b)  it correctly describes the values
returned. Scoring: Our original idea was that each of the three parts was worth 2
points. But we had trouble deciding what to do about answers based on the
theory of special cases in the code. Many students seemed to be imagining
a procedure written like this: (define (arabic num) (cond ((empty? (bf num)) (lettervalue (first num))) ((equal? (firsttwo num) 'IV) (+ 4 (arabic (bf (bf num))))) ((equal? (firsttwo num) 'IX) (+ 9 (arabic (bf (bf num))))) ((equal? (firsttwo num) 'XL) (+ 40 (arabic (bf (bf num))))) ... (else (+ (lettervalue (first num)) (arabic (bf num)))))) but with some of the numeric values incorrect. Is this a plausible program? Certainly not if the program was written by a
competent professional. But it's something a beginning student might write.
So the debugging process has to take into account who wrote the program! In the end, we identified several common types of answers, and assigned scores
to each. We also read the answers to all three parts at once, because a lot
of students gave bad answers to (b) and then put a perfectly good answer to
(b) in the space intended for (c). We credited these as (b) answers. Here are the cases: (a) smallbeforelarge
(b) larger letter value counted twice
(c) skips one letter instead of two This was the answer we wanted; it got 6 points (2+2+2). (a) smallbeforelarge
(b) larger letter value counted twice
(c) multiplies larger letter by 2 This got 4 points (2+2+0). (a) smallbeforelarge
(b) larger letter value counted twice
(c) just a restatement of (b) with no basis in the procedure. This got 4 points (2+2+0). (a) Only some smallbeforelarge pairs fail.
(b) Anything, if consistent with (a).
(c) Specific incorrect pair values built into the program. This got 4 points (2+0+2). (a) smallbeforelarge.
(b) Anything inconsistent with the data.
(c) Plausible reason for the incorrect (b) behavior. This got 4 points (2+0+2). (a) smallbeforelarge
(b) "bigger than it should be"
(c) anything that doesn't redeem (b) This got 3 points (2+1+0). (a) smallbeforelarge
(b) incorrect
(c) incorrect This got 2 points (2+0+0). (a) incorrect
(b) correct
(c) correct This quite rare combination got 4 (0+2+2).  If you don't like your grade, first check these solutions. If we
graded your paper according to the standards shown here, and you
think the standards were wrong, too bad  we're not going to grade
you differently from everyone else. If you think your paper was
not graded according to these standards, bring it to Brian or your TA.
We will regrade the entire exam carefully; we may find errors that
we missed the first time around. :) If you believe our solutions are incorrect, we'll be happy to discuss
it, but you're probably wrong!
...
View
Full
Document
 Fall '08
 Harvey

Click to edit the document details