CS3 SPRING 2001 MIDTERM #1 ANSWERS (prepared by Danny Yoo) Question 1 We're given the initial defintions: (define mantra '(cal is great)) (define (weird a b c) '(/ 100 a)) (define (mult-10 w) (word w 0)) (define (my-if the-test the-then the-else) (cond (the-test the-then) (else the-else))) For each part, we took off: -2 for errors with an incorrect explanation, -1 for quoting problems -1 for spotting an error, but not given an explanation Let's go through each part: (* (+ 2 (/ 7 1) (*) ) ) evaluates to 10. What caught a few people off-guard was the subexpression: (*) which evaluates to 1, since 1 is the identity element for multiplication. --- (equal? (quote (1)) (se (1)) ) is an error, because the subexpression: (se (1)) tries to unsuccessfully call 1 as a function. --- (if (= 1 '1) + -) evaluates to a PROCEDURE. --- (if < 3 4) evaluates to 3. '<' is a procedure. Since this is not false, it's a true value. --- (and (if #f #f #t) 'a '(b) (not (= 1 2)) mantra) evaluates to the sentence (CAL IS GREAT). A few people forgot to remember that 'mantra' itself was a global variable bound to the sentence '(cal is great). --- (bl (bf mantra)) evaluates to the sentence (IS). --- (lambda () m a n 't 'r 'a) evaluates to a PROCEDURE. A few people said that this expression would error because neither m, a, nor n were defined. Although these symbols aren't defined, Scheme doesn't look into a function's body until it's called. This explains why certain typos don't immediately cause errors in Scheme. For example, the following function: (define (second x) (furst (butfurst x))) doesn't break until we try to call it. --- (weird 0 1 2) returns the sentence (/ 100 a). Quotation in front of a sentence prevents any of its components from being evaluated: they're treated as literal symbols. So, within the weird definition: (define (weird a b c) '(/ 100 a)) regardless of what input we feed weird, we will always get back the sentence (/ 100 a). --- (every mult-10 '(weird 10 2 3) )) evaluates to the sentence (WEIRD0 100 20 30). People generally had no problems with this one. --- (accumulate - (every multi 10 '(3 2 1))) evaluates to 20. The subexpression (every multi 10 '(3 2 1)) returns to us the list (30 20 10) which, when we feed to the accumulation, evaluates the equivalent expression: (- 30 (- 20 10)) ====== Question 2: The definition we've given: (define mutual-fun (lambda (f g) (if (< (f 1) (g 1)) f g))) is a function in explicit lambda form, and can be analyzed more easily if we transform it to the equivalent implicit lambda form: (define (mutual-fun f g) (if (< (f 1) (g 1)) f g)) Part a: Because we have subexpressions (f 1) and (g 1), f and g must be themselves functions that take in one argument. Because mutual-fun returns either f or g back, the range of mutual-fun are functions. One other thing to notice is that the results of calling f and g on argument 1 will be compared with the less-than operator. This implies that when f and g are called, they need to return back numbers. Bonus points were awarded for giving more information about the domain: +1 for explaining that f and g must be unary (take in one argument). +1 for saying that f and g take in numbers. +1 for saying that f and g must return numbers. --- Part b: ((mutual-fun sqrt sqrt) 4) is one possible answer. Because mutual-fun needs to take in two functions, we need to provide it an appropriate function to work with. Other possible answers include: ((mutual-fun abs sqrt) 4), ((mutual-fun first sqrt) 4), and there are many other answers that are possible. We awarded: +3 for having an expression like the above. +2 for realizing that mutual-fun took in two functions, where the result of calling mutual-fun must itself be called. +1 for having two open parentheses or coming up with functions. ====== Question 3 The crucial missing test is for negative days or inputting more days than what's in a particular month. We can correct this by adding one conditional clause in the third box which checks both upper and lower bounds on a date: ((or (< (date-in-month date) 1) (> (date-in-month date) (days-in-month date))) 'illegal-day-supplied) Placing this answer in any other box will deduce two points, because then (date-in-month date) has the possibility of returning the value. +1 Bonus point for having both bounds correct, in the right box. -2 if the answer's in the wrong box. -1 if the case study function days-in-month wasn't used; if you write out the functionality of days-in-month in full, you're not taking advantage of the case study. -3 if, instead of using days-in-month, an upper bound of 31 days was used instead for each month. However, acknowledgement that this is a rough upper bound will add +1. -3 if only a lower bound (< (days-in-month) 0) was checked. -1 if a nondescriptive name is used. -1 for trivial errors. ====== Question 4 In this question, we were asking you to be able to trace code by hand, a critical skill for CS3. The ability to "put yourself in the computers place" allows you to figure out what's wrong with part a. Part b requires you to think about why, even after fixing part a, it's still wrong and come up with a creative solution to fix it -- here we were testing not just a knowledge of scheme but of abstract problem-solving ability: "Here's what I want to do (in my head), here's code which doesn't do it, how to I fix it?" Part a: Change line 2 with the line: (newone total) To make the notation easier to read, we'll give the name f to the function returned by lambda: (define (f total newone) (+ (total (* newone newone)))) If we apply the function f on the sentence '(1 2 3), we can see the following: (f 1 (f 2 3)) ==> (f 1 (+ 2 (* 3 3))) ==> (f 1 11) ==> (+ 1 (* 11 11)) ==> (+ 1 121) ==> 122 which is how 122 gets returned. The idea is that as we're accumulating starting on the right hand side, we keep a running sum of squares that we've accumulated so far. The problem is that we've misordered the function's arguments; we're accidently applying the squaring on the running total, which can be corrected by reordering the parameter list. Part b: Replacing line #6 with the expression: (se sent 0) will fix our problem. The other bug left, given that we make the correction in Part a, is that we haven't squared the initial rightmost element of our sentence. The way sum-of-squares works is to go through, one by one, and square the left element and add it to the total. Well, you probably realize that you need to start "total" out with some value...what should it be? Well, we're adding things to the total, so since the identity of + is 0, we need to initially set total to 0. The way to do that is to have total be 0 the FIRST time you call lambda. In order to make things work out correctly, we append an initial 0 to the sentence. ====== Question 5 Part a: The expression (mystery '()) evaluates to the sentence: (1) There was no partial credit for this part. Many people had not expected the subexpression: (every add-one '()) to return the empty sentence, and the majority of answers guessed that (mystery '()) would return the sentence: (1 1) However, as long as answers were consistent with the rest of the problem, we did not penalize the other parts. --- Part b: (mystery (mystery '())) evaluates to the sentence: (1 2) If your answer to Part a: was the sentence (1 1), then we accepted the sentence: (1 2 2) as a consistent answer which received full credit. --- Part c: Following parts 1 and 2, the idea is to repeatedly call the mystery function on the empty sentence: (define (1-to-b b) ((repeated mystery b) '())) If your answer to part a was the sentence (1 1), then your answer here must compensate for the trailing duplicated number at the end of the sentence: (define (1-to-b b) (butlast ((repeated mystery b) '()))) which we accepted. We awarded: +1 for using repeated and mystery +2 for using the correct form of repeated +3 for ((repeated function num) args) +4 for a small error +5 for a correct solution Some students didn't see the connection between the first parts of this problem and part c, but were still able to write 1-to-b with some creativity: (define (helper sent) (se sent (+ 1 (last sent)))) (define (1-to-b b) ((repeated helper (- b 1)) '(1))) Others felt very uncomfortable about doing an every across an empty sentence, but still realized how mystery worked, so they used this solution: (define (1-to-b b) ((repeated mystery (- b 1)) '(1))) which also works. --- Part d: This question could be answered even if parts a-c were left blank. Here, we're asking you to take a sentence of some numbers and reduce it somehow. The picture from SS p. 110 "Choosing the right tool" tells you the answer. Here are two possible answers to a-to-b: (define (a-to-b a b) (keep (lambda (n) (>= n a)) (1-to-b b))) (define (a-to-b a b) (every (lambda (n) (+ -1 a n)) (1-to-b (+ 1 (- b a))))) The first constructs the whole range of numbers from 1-to-b, and then filters out the numbers that are less than a. The second constructs the numbers from the difference of a to b and then adds enough to each one to come up with the same answer. We gave three points of partial credit for the solution: (define (a-to-b a b) ((repeated bf (- a 1)) (1-to-b b))) because of the prohibited use of repeated.