Question 1 - Mutation - INSERT-AFTER!(8 pts) ~~~~~~~~~~~ There were three main cases you needed to test for. 1) If the list is null, you can't do any inserts so it should return 0 2) Test to see if the first element is equal to what you want to insert after. Then do the insertion and add one to the insertion count 3) It's not equal so just recursively do the insertion on the rest of the list. So here is the solution. (define (insert-after! e x lst) (cond ((null? lst) 0) ((equal? (car lst) e) (set-cdr! lst (cons x (cdr lst))) (+ 1 (insert-after! e x (cddr lst)))) (else (insert-after! e x (cdr lst))))) Most people created a local variable and then created a helper which mutated this counter. Some people did this: (define (insert-after! e x lst) (let ((counter 0)) (cond ((null? lst) counter) ((equal? (car lst) e) (set-cdr! lst (cons x (cdr lst))) (insert-after! e x (cddr lst)) (set! counter (+ counter 1))) (else (insert-after! e x (cdr lst)))))) But what's incorrect about this solution? Well every time you do a recursive call to insert-after! you will reset the counter to 0! So this solution will only return 0 or 1 depending on which clause of the cond you are in. Some people did this when inserting the element: (set-cdr! (car lst) (cons x (cdr lst))) A big no no. What is the car of the list? An atomic expression! Can you set the cdr of an atom? NO! This is a huge mistake. +1: doing the mutation of the list correctly (set-cdr! ..) +2: correctly cons-ing the element to the rest of the list +2: recursive call on the (cddr lst) in the equal? clause of the cond +2: return value +1: else clause recursion -2: infinite loop (doesn't do (cddr lst)) -2: set-cdr! on atom -2: wrong return value (ie doing let case stated above) -2: no mutation of the list -1: extraneous cases -1: doesn't check for the null list base case -1: cdr-ing a null lst Question 2 - Metacircular Evaluator - EVAL-ASSIGNMENT (8 pts) ~~~~~~~~~~ So the change to eval-assignment now evaluates the first argument before setting it to the second argument. So it'll do the variable lookup on the evaluated first expression, whatever it may be, and set that. ;;; M-Eval input: (define sloth 'stupid) ;;; M-Eval input: (define stupid 0) ;;; M-Eval input: (set! sloth 2) So here when this set! is evaluated it will evaluate sloth to be 'stupid. Then in set-variable-value! it will do a variable lookup on 'stupid which is defined in the global environment as 0. So the variable IS bound so stupid will be set to 2. ;;; M-Eval input: sloth This will return you the word stupid ;;; M-Eval input: stupid This will return you 2 since that's what the set! had done in the third line. ;;; M-Eval input: ((lambda (elt) (set! (car '(elt)) (* elt elt)) elt) 5) So when you call this procedure elt is bound to 5 as usual. Once again the set! will evaluate the first argument so you take the car of '(elt) which is elt and you multiply (* elt elt) so you set! elt to 25 and then you return elt which is 25. So this will return you 25. A lot of people said error to this which of course is incorrect. We got a lot of unbound variable e, so some people thought that '(elt) evaluated to a word, which of course is incorrect. ;;; M-Eval input: (define carlos 'david) ;;; M-Eval input: (define david 17) ;;; M-Eval input: (set! carlos (+ carlos 1)) So you look up carlos to be david and then you try to add david to 1 which is the error. So we took off a point if you said that the error was carlos is not a number. The reason behind this was that it is not carlos but david that is not a number. So we also took a point off for the next line ;;; M-Eval input: carlos if you wrote anything that was incorrect. We had said that if the previous line was an error you should leave the next line blank. So if you left it blank you would have gotten a point. So we accepted the word david. ;;; M-Eval input: (set! (+ 3 15) 'eighteen) This once again, same error. There is no variable 18 bound. Most people got this incorrect thinking that it was an error because you cannot change a number's value. Also people thought you could change the value of 18 which is also incorrect. ;;; M-Eval input: 18 We accepted 18 or a blank. ;;; M-Eval input: (let ((sent '(in the usa))) (let ((temp (car sent))) (set! temp (cadr sent)) sent)) This of course errors because temp is the word in and the variable lookup on in. So the official answer would be Unbound Variable: IN. +1: each blank -1: if error message was incorrect -1: if a blank has an incorrect value Question 3 - Streams - CALL-ON-PREV (8 pts) ~~~~~~~~~~ There were a few possible solutions. (define (first-n str n) (if (= n 1) (list (stream-car str)) (cons (stream-car str) (first-n (stream-cdr str) (- n 1))))) (define (call-on-prev func x) (define (help n) (cons-stream (apply func (first-n str n)) (help (+ n 1)))) (define str (cons-stream x (help 1))) str) ;or (define (call-on-prev func x) (define (help n str) (cons-stream (apply func (first-n str n)) (help (+ n 1) (call-on-prev func x)))) (cons-stream x (help 1 (call-on-prev func x)))) Without a helper can be done as follows: (define (call-on-prev func x) (define (helper list-so-far) (let ((cur-element (apply func list-so-far))) (cons-stream cur-element (helper (append list-so-far (list cur-element)))))) (cons-stream x (helper (list x)))) + 2 points for applying the function recursively over the stream + 2 points for keeping track of the ENTIRE previous stream + 2 points for "apply"ing the function to the stream properly + 2 points for lack of other errors.. Solutions that computed the fixed-point stream, ie. x, f(x), f(f(x)), ... were given two points. Solutions that applied the function to the last TWO elements of the stream, like fibonacci does, received 3 points. Question 4 - Metacircular Evaluator - FOR LOOP (8 pts) ~~~~~~~~~~ (define (for? exp) (tagged-list? exp 'for)) (define (for-nonbody exp) (cadr exp)) (define (for-variable exp) (car (for-nonbody exp))) (define (for-initial exp) (cadr (for-nonbody exp))) (define (for-condition exp) (caddr (for-nonbody exp))) (define (for-increment exp) (cadddr (for-nonbody exp))) (define (for-body exp) (cddr exp)) (define (eval-for exp env) (define (loop env) (if (true? (mc-eval (for-condition exp) env)) (begin (eval-sequence (for-body exp) env) (mc-eval (for-increment exp) env) (loop env)) 'ok)) (loop (extend-environment (list (for-variable exp)) (list (mc-eval (for-initial exp) env)) env))) +1: getting the selectors right (all or nothing) +1: making local by extending the environment +1: evaluating +1: evaluating before and +1: using TRUE? +1: evaluating +2: looping, for correct order of evaluation of components -2: using SET! or using a LET thinking that this affects the metacircular Scheme environment -2: not updating the value of from iteration to iteration Question 5 - Lazy Evaluator - STRICT/UNSTRICT (8 pts) ~~~~~~~~~~ There are two things that needed to be changed: Line 10 needed to be fixed to get the param name Line 11 needed to be fixed to only delay the non-strict args (and force the others). Line 10 was easier. Most people changed it like so: Line 10: (map cadr (procedure-parameters procedure)) Line 11 was harder. The best solutions used a 2-list map on the arg and param lists: Line 11: (map (lambda (param arg) (if (tagged-list param 'strict) (actual-value arg env) (delay-it arg env))) (procedure-parameters procedure) arguments) We saw a lot of procedures that modified list-of-delayed-values or used a helper instead of map: Line 11: (helper arguments (procedure-parameters) env) (define (helper args params env) (cond ((null? args) '()) ((equal? (caar params) 'strict) (cons (actual-value (car args) env) (helper (cdr args) (cdr params) env))) (else (cons (delay-it (car args) env) (helper (cdr args) (cdr params) env))))) Here's how we gave points: +2: Doing something for line 10 that worked. +2: Did some kind of iteration down either the param or arg list +2: Attempted to delay/eval based on param type +2: Perfect And here's how we took them away: -2: Thinking that the arg list contained the 'strict'/'nonstrict' words. -1: Mishandling the list operations -1: Minor recursive errors.