#lang racket ;; This will not run due to undefined terms. ;;;;;;;;; ;; 4.2 ;; ;;;;;;;;; ;; a. Since the predicate for recognizing procedure applications was 'any compound ;; expression not already parsed', the repl will mistake any compound expression with ;; a lower clause as a procedure application. For example, upon encountering ;; (define x 3), the repl will search the environment for a procedure named define. ;; b. The predicate and selectors for procedure applications must change to: (define (application? expr) (tagged-list? expr 'call)) (define (operator expr) (second expr)) (define (operands expr) (rest (rest expr))) ;;;;;;;;; ;; 4.3 ;; ;;;;;;;;; ;; Assume we have all the selectors and constructors, as in the book. ;; Assume also that procedure applications are tagged lists beginning with 'call. ;; Then the data-directed version of my-eval looks like this: (define eval-table (make-hash)) (define (put type item) (hash-set! eval-table type item)) (define (get type) (hash-ref eval-table type)) (define (type expr) (and (pair? expr) (symbol? (first expr)) (first expr))) (define (my-eval expr env) ; data-directed style (cond [(self-evaluating? expr) expr] [(variable? expr) (lookup-variable-value expr env)] [else ((get (type expr)) expr env)])) (define (install-evaluation-rules) (define (quote-rule expr env) (text-of-quotation expr)) (define (assignment-rule expr env) (eval-assignment expr env)) (define (definition-rule expr env) (eval-definition expr env)) (define (lambda-rule expr env) (make-procedure (lambda-parameters expr) (lambda-body expr) env)) (define (conditional-rule expr env) (eval-if expr env)) (define (begin-rule expr env) (eval-sequence (begin-actions expr) env)) (define (application-rule expr env) (my-apply (my-eval (operator expr) env) (list-of-values (operands expr) env))) (define (cond-rule expr env) (my-eval (cond->if expr) env)) (put 'quote quote-rule) (put 'set! assignment-rule) (put 'define definition-rule) (put 'lambda lambda-rule) (put 'if conditional-rule) (put 'begin begin-rule) (put 'call application-rule) (put 'cond cond-rule) ) ;;;;;;;;; ;; 4.4 ;; ;;;;;;;;; ;; AND ;; Add this clause to my-eval: [(and? expr) (eval-and expr env)] (define (and? expr) (tagged-list? expr 'and)) (define (and-operands expr) (rest expr)) (define (make-and sequence) (cons 'and sequence)) (define (eval-and expr env) (define ops (and-operands expr)) (cond [(empty? ops) 'true] ; (and) should be true [else (define val (my-eval (first ops) env)) (define rest-ops (rest ops)) (cond [(empty? rest-ops) val] ; (and x) should be x [(eq? val 'false) 'false] [else (eval-and (make-and rest-ops) env)])])) ;; OR ;; Add this clause to my-eval: [(or? expr) (eval-or expr env)] (define (or? expr) (tagged-list? expr 'or)) (define (or-operands expr) (rest expr)) (define (make-or sequence) (cons 'or sequence)) (define (eval-or expr env) (define ops (or-operands expr)) (cond [(empty? ops) 'false] ; (or) should be false [else (define val (my-eval (first ops) env)) (define rest-ops (rest first-ops)) (cond [(empty? rest-ops) val] ; (or x) should be x [(not (eq? val 'false)) val] [else (eval-or (make-or rest-ops) env)])])) ;; AND AS A DERIVED EXPRESSION (define (and->if expr) (expand-and-operands (and-operands expr))) (define (expand-and-operands ops) (cond [(empty? ops) 'true] [else (define first-op (first ops)) (define rest-ops (rest ops)) (cond [(empty? rest-ops) first-op] [else (make-if first-op (and->if (make-and rest-ops)) 'false)])])) ;; test (and->if '(and x1 x2 x3)) ;; '(if x1 (if x2 x3 false) false) ;; OR AS A DERIVED EXPRESSION (define (or->if expr) (expand-or-operands (or-operands expr))) (define (expand-or-operands ops) (cond [(empty? ops) 'false] ; (or) should return false [else (define first-op (first ops)) (define rest-ops (rest ops)) (cond [(empty? rest-ops) first-op] ; (or x) should return x [else (make-if first-op first-op ; inefficient second evaluation (or->if (make-or rest-ops)))])])) ;; test (or->if '(or y1 y2 y3)) ;; '(if y1 y1 (if y2 y2 y3)) ;; OR AS A DERIVED EXPRESSION, WITH ACCESS TO THE ENVIRONMENT (define (or->if2 expr env) (expand-or-operands2 (or-operands expr) env)) (define (expand-or-operands2 ops env) (cond [(empty? ops) 'false] [else (define val (my-eval (first ops) env)) (define rest-ops (rest ops)) (cond [(empty? rest-ops) val] [else (make-if val ; no inefficient second evaluation val (or->if2 (make-or rest-ops)))])])) ;;;;;;;;; ;; 4.5 ;; ;;;;;;;;; (define (cond? expr) (tagged-list? expr 'cond)) (define (cond-clauses expr) (rest expr)) ; regular cond clause (define (cond-predicate clause) (first clause)) (define (cond-actions clause) (rest clause)) ; alternate test clause (define (cond-alternate-clause? clause) (eq? (second clause) '=>)) (define (cond-test clause) (first clause)) (define (cond-recipient clause) (third clause)) ; else clause (define (cond-else-clause? clause) (eq? (cond-predicate clause) 'else)) ; derive cond from if, needs access to the environment (define (cond->if expr) (expand-clauses (cond-clauses expr))) (define (expand-clauses clauses) (cond [(empty? clauses) 'false] ; no else clause [else (define first-clause (first clauses)) (define rest-clauses (rest clauses)) (cond [(cond-else-clause? first-clause) (if (empty? rest-clauses) (sequence->exp (cond-actions first-clause)) (error "ELSE clause isn't last -- COND->IF" clauses))] [(cond-alternate-clause? first-clause) (define test (cond-test first-clause)) (define recipient (cond-recipient first-clause)) (make-if test (list recipient test) (expand-clauses rest-clauses))] [else (make-if (cond-predicate first-clause) (sequence->exp (cond-actions first-clause)) (expand-clauses rest-clauses))])])) ;; tests (cond->if '(cond [a 1] [b 2] [else 3])) ;; '(if a 1 (if b 2 3)) (cond->if '(cond [a 1] [(list 1 2) => car] [else 3])) ;; '(if a 1 (if (list 1 2) (car (list 1 2)) 3)) ;;;;;;;;; ;; 4.6 ;; ;;;;;;;;; ;; Add this clause to my-eval: [(let? expr) (my-eval (let->combination expr) env)] (define (let? expr) (tagged-list? expr 'let)) (define (let-bindings expr) (second expr)) (define (let-parameters expr) (map first (let-bindings expr))) (define (let-expressions expr) (map second (let-bindings expr))) (define (let-body expr) (sequence->exp (rest (rest expr)))) (define (let->combination expr) (cond [(empty? (let-bindings expr)) ; do not unnecessarily lambda wrap (let-body expr)] [else (cons (make-lambda (let-parameters expr) (let-body expr)) (let-expressions expr))])) (define (make-let bindings body) (cons 'let (list bindings body))) ;; test (let->combination '(let () (+ 1 2))) ;; '(+ 1 2) (let->combination '(let ((a 1) (b 2)) (+ a b))) ;; '((lambda (a b) (+ a b)) 1 2) ;;;;;;;;; ;; 4.7 ;; ;;;;;;;;; ;; I think it's fine to implement let* as a derived form. my-eval will have to be ;; called recursively to evaluate the inner let's, but that's fine, my-eval is meant ;; to be recursive. ;; Add this clause to my-eval: [(let*? expr) (my-eval (let*->nested-lets expr) env)] (define (let*? expr) (tagged-list? expr 'let*)) (define (let*-bindings expr) (second expr)) (define (let*-body expr) (sequence->exp (rest (rest (expr))))) (define (make-let* bindings body) (cons 'let* (list bindings body))) (define (let*->nested-lets expr) (define bindings (let*-bindings expr)) (cond [(empty? bindings) (let-body expr)] [else (make-let (list (first bindings)) (let*->nested-lets (make-let* (rest bindings) (let-body expr))))])) ;; test (let*->nested-lets '(let* () (+ 1 2))) ;; '(+ 1 2) (let*->nested-lets '(let* ((a 1) (b (+ a 1))) (+ a b))) ;; '(let ((a 1)) (let ((b (+ a 1))) (+ a b))) (let->combination (let*->nested-lets '(let* ((a 1) (b (+ a 1))) (+ a b)))) ;; '((lambda (a) (let ((b (+ a 1))) (+ a b))) 1) ;;;;;;;;; ;; 4.8 ;; ;;;;;;;;; ;; Add this clause to my-eval: [(named-let? expr) (my-eval (named-let->sequence expr) env)] (define (named-let? expr) (and (let? expr) (not (list? (second expr))))) (define (named-let-name expr) (second expr)) (define (named-let-bindings expr) (third expr)) (define (named-let-parameters expr) (map first (named-let-bindings expr))) (define (named-let-expressions expr) (map second (named-let-bindings expr))) (define (named-let-body expr) (sequence->exp (rest (rest (rest expr))))) ;; At first I tried converting the named let to a regular let by adding the name-body ;; pair as a new let-binding, but that did not work. ;; For example, my original wrong named-let->sequence took the expression below, ;; which, when unquoted, evaluates to (fib 10) = 55; and produced an expression ;; that, when unquoted, produced a 'fib-iter undefined' error: '(let fib-iter ((a 1) (b 0) (count 10)) (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) ;; '((lambda (fib-iter a b count) ;; (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) ;; (lambda (a b count) (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) ;; 1 ;; 0 ;; 10) ((lambda (fib-iter a b count) (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) (lambda (a b count) (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) 1 0 10) ;; . . fib-iter: undefined; ;; cannot reference undefined identifier ;; So this version of named-let->sequence creates a sequence of two expressions, ;; one to define the named function, and the second to apply it to the named-let ;; expressions. (define (named-let->sequence expr) (define bindings (named-let-bindings expr)) (cond [(empty? bindings) (named-let-body expr)] [else (list 'begin (list 'define ; first define the named function (cons (named-let-name expr) (named-let-parameters expr)) (named-let-body expr)) (cons (named-let-name expr) ; then apply it to the expressions (named-let-expressions expr)))])) ;; test (named-let->sequence '(let f () (+ 1 2))) ;; '(+ 1 2) (named-let->sequence '(let fib-iter ((a 1) (b 0) (count 10)) (if (zero? count) b (fib-iter (+ a b) a (sub1 count))))) ;; '(begin ;; (define (fib-iter a b count) ;; (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) ;; (fib-iter 1 0 10)) (begin (define (fib-iter a b count) (if (zero? count) b (fib-iter (+ a b) a (sub1 count)))) (fib-iter 1 0 10)) ;; 55 ;;;;;;;;; ;; 4.9 ;; ;;;;;;;;; ;; THE WHILE LOOP '(while do ) ;; is a derived form for: '(if (begin (while do )) done) ;; Add this clause to my-eval: [(while? expr) (my-eval (while->if expr) env)] (define (while? expr) (tagged-list? expr 'while)) (define (while-test expr) (second expr)) (define (while-body expr) (fourth expr)) (define (make-while test body) (list 'while test 'do body)) (define (while->if expr) (make-if (while-test expr) (list 'begin (while-body expr) (make-while (while-test expr) (while-body expr))) 'done)) ;; test (while->if '(while (positive? x) do (begin (log! x) (set! x (sub1 x))))) ;; '(if (positive? x) ;; (begin ;; (begin (log! x) (set! x (sub1 x))) ;; (while (positive? x) do (begin (log! x) (set! x (sub1 x))))) ;; done) ;;;;;;;;;; ;; 4.10 ;; ;;;;;;;;;; ;; For example we could change the syntax for assignment from: (set! ) ;; to: (set! ) ;; simply by changing the following procedures: (define (assignment-variable expr) (third expr)) (define (assignment-value expr) (second expr))