;;;;;; #1 ;;;;;; 4.3 ;assume put and get defined (define (self-evaluating? exp) (cond ((number? exp) #t) ((string? exp) #t) (else #f))) (define (eval exp env) (if (self-evaluating? exp) exp (let ((proc (get (car exp)))) (if proc (proc exp env) (error "Unknown expression type -- EVAL " exp))))) ;;;;;; 4.6 ;assume the let using here is defined in underlying scheme ;general form of let ;(let ((name1 value1) ; (name2 value2) ; ...) ; body) ;parameter list is the cadr of the expression ;the lambda body is cddr of the expression ;actual parameter to the corensponding lambda is the (map car parameter-list) ;the value of these parameters at invocation time is (map cadr parameter-list) (define (let->combination exp env) (let ((parameter-list (cadr exp)) (body (cddr exp))) (let (proc (make-procedure (map car parameter-list) body env)) (apply proc (map cadr parameter-list))))) ;;;;;; 4.7 ;we can transform the general form of let* into a let expression ;general form of let* ;(let* ((name1 value1) ; (name2 value2) ; ... ; (name-n value-n)) ; body) ;transform it into the general form of let ;(let ((name1 value1)) ; (let ((name2 value2)) ; ... ; (let ((name-n value-n)) ; body))) (define (let*->nested-lets exp) (let ((parameter-list (cadr exp)) (body (cddr exp))) (define (inner parameter-list body) (if (null? (cdr parameter-list)) (let parameter-list body) (let (list (cadr parameter-list)) (inner (cdr parameter-list) body)))) (inner parameter-list body))) ;;;;;; 4.10 ;assume we invent a new syntax for lambda ;(lambda (body) (parameter-list)) ;example: (lambda (x y) (print 'hello) (+ x y)) ;will become (lambda ((print 'hello) (+ x y)) (x y)) (define (lambda? exp) (tagged-list? exp 'lambda)) (define (lambda-parameters exp) (caddr exp)) (define (lambda-body exp) (cadr exp)) (define (make-lambda body parameters) (cons 'lambda (cons parameters body))) ;there is no need to change the definition of eval or apply ;;;;;; 4.13 ;specification for make-unbound! ;takes two arguments: the variable name and the enviroment ;check to see if it exist in the current enviroment, if it is, remove. then return 'ok ;if not, call make-unbound! again using variable name and the one-level-up enviroment ;if no one-level-up enviroment, return an error ;note: this make-unbound must work with the other procedures defined in the book ;espacially make-frame, frame-variables, frame-values (define (make-unbound! var env) (if (eq? env the-empty-environment) (error "Unbound variable -- MAKE-UNBOUND! " var) (let ((frame (first-frame env))) (let ((vars (frame-variables frame)) (vals (frame-values frame))) (define (scan var var-list val-list pre-var-list pre-val-list) (cond ((null? var-list) #f) ((eq? var (car var-list)) (if (eq? nil pre-var-list) (begin (set-car! frame (cdr var-list)) (set-cdr! frame (cdr val-list))) (begin (set-cdr! pre-var-list (cdr var-list)) (set-cdr! pre-val-list (cdr val-list)))) #t) (else (scan var (cdr var-list) (cdr val-list) var-list val-list)))) (let (result (scan var vars vals nil nil)) (if result 'ok (make-unbound! var (enclosing-environment env)))))))) ;;;;;; 4.14 ;because Louis's map is a primitive procedure, when she redefine the map, it's no longer ;a primitive procedure, but the 'map tag remain as a primitive procedure. so when we ;try to apply map to the arguements, we call apply-primitive-procedure which cause the ;trouble. ;;;;;; 4.15 ;if (halts? p p) returns #t, that means p returns a value, #f means an error or run forever ;(try try) will call (halts? try try), suppose it returns #t, we then runs forever. ;suppose it returns false, we get 'halted. Notice that no matter what (halts? try try) ;returns, (try try) will act in opposite way. Thus, we have a contridiction, which proves ;it's impossible to write halts? ;;;;;; 4.23 ;book's code (define (analyze-sequence exps) (define (sequentially proc1 proc2) (lambda (env) (proc1 env) (proc2 env))) (define (loop first-proc rest-procs) (if (null? rest-procs) first-proc (loop (sequentially first-proc (car rest-procs)) (cdr rest-procs)))) (let ((procs (map analyze exps))) (if (null? procs) (error "Empty sequence -- ANALYZE ")) (loop (car procs) (cdr procs)))) ;Hacker's code (define (analyze-sequence exps) (define (execute-sequence procs env) (cond ((null? (cdr procs)) ((car procs) env)) (else ((car procs) env) (execute-sequence (cdr procs) env)))) (let ((procs (map analyze exps))) (if (null? procs) (error "Empty sequence -- ANALYZE ")) (lambda (env) (execute-sequence procs env)))) ;the main difference is, as Ator expalins, Hacker's code returns a procedure takes a parameter, env, ;then, it call a procedure (execute-sequence procs env). ;the book's code returns an analyzed procedure as a whole takes one argument, the enviroment. ;for example, only one procedure in sequence, book's version will generate procs using ;(map ananlyze exps), the call (loop (car procs) (cdr procs)), because (cdr procs) is '(), ;we get first-procs ;hacker's version generate procs using (map ananlyze exps) also, but it returns a lambda which ;take an env as argument. we indeed analyzed the expression, but each time we will still call ;execute-sequence and evaluate execute-sequence. ;when we have two arguments, the book's version returns the analyzed procedure as a whole, ;comparing to hacker's version, which analyze each expression and then return a procedure ;using these analyzed expression as arguement. Therefore, Hacker's code works correctlly, but inefficient. ;;;;;; 4.24 ;first of all, I understand the concept of this section, which is to generate a new computer ;language using an existing one. Yet, using the same language to perform the same language is ;a kind of funny and confusing. Though the book gives certain reasons, I still cannot see the ;benifit of using lisp to transform lisp. In contrast, I like the project #4 better because it ;is more logical. ;anyway, I load some of the book's code into scheme, and tried some tests, but seems very hard to ;tell the fraction of time. Maybe a really long sequence will do the trick here. ;;;;;; 4.26 ;first, to support Ben's argument, generating the procedures for evaluation. ;add the following line to the cond inside eval procedure ((unless? exp) (eval-unless exp env)) ;now, define the procedures (define (unless? exp) (tagged-list? exp 'unless)) (define (eval-unless exp env) (let ((condition (cadr exp)) (usual-value (caddr exp)) (exceptional-value (cadddr exp))) (if (eval condition env) (eval usual-value env) (eval exceptional-value env)))) ;this intepertation maybe inefficient, but should work, Ben's idea is acceptable ;then, it's Alyssa's turn. ;suppose we have a procedure foo (define (foo f1 f2) (lambda (a b c) (f1 a b c) (f2 a b c) (list a b c))) ;then we evaluate ((foo unless some-other-procedure) (= 0 0) (+ 2 3) (/ 1 0)) ;this will be an example of the advantage of using unless as a procedure. ;;;;;; 4.28 ;inside the body of eval ((application? exp) (apply (actual-value (operator exp) env) (operands exp) env)) (define (actual-value exp env) (force-it (eval exp env))) (define (apply procedure arguments env) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure (list-of-arg-values arguments evn))) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) (list-of-delayed-args arguments env) (procedure-environment procedure)))) (else (error "Unknown procedure type -- APPLY " procedure)))) ;assume now we say (eval '(foo 1)) and foo is defined as following (define (foo x) (* x 100)) ;without the help of actual-value, the (eval foo env) will just delay the ;evaluation, so when we in apply, primitive-procedure? is #f, compound-procedure ;is #f also, so we got an error. this is when the actual-vale is required. ;;;;;; 4.30 ;;; a. ;Ben's right because we forced the proc first and return a new procedure. ;notice the change in eval ((application? exp) (apply (actual-value (operator exp) env) (operands exp) env)) ;;; b. ;(p1 1) gives 1 in original eval-sequence, but it returns (1 2) in Cy's version ;(p2 1) gives 1 in original eval-sequence, but it returns (1 2) in Cy's version ;;; c. ;this is true because with the modified eval-sequence because it evaluates the ;sequence one by one, as long as it's not the last expression, it continues to ;evaluate. whenever there is a print or display, it will display to the screen. ;;; d. ;I think Cy's version is better because it just works correctly to handle the set! ;and other assignment statements. ;;;;;; 4.33 (define (text-of-quotation exp) (let ((text (cadr exp))) (if (not (pair? text)) text (begin (define (generate arg) (if (null? arg) '() (cons (first arg) (generate (bf arg))))) (generate text))))) ;cons is the new definition as lazy list, but, first, bf are still used to deal ;with sentance. ;;;;;; #2 ;the idea I have is to transform the definition, for example ;the transformed foo will be (define (foo num alist) (begin (if (not (integer? num)) (error "Error: wrong argument type -- " num)) (if (not ((lambda (x) (not (null? x))) alist)) (error "Error: wrong argument type -- " alist)) (nth num alist))) ;;;now, define the necessary codes (define (eval-definition exp env) (define-variable! (definition-variable exp) (eval (definition-value exp env) env) env) 'ok) (define (definition-variable exp) (if (symbol? (cadr exp)) (cadr exp) (caadr exp))) (define (definition-value exp env) (cond ((symbol? (cadr exp)) (caddr exp)) ((no-sub-list? (cdadr exp)) (make-lambda (cdadr exp) (cddr exp))) (else (let ((parameters (find-parameters (cdadr exp))) (check-proc (find-check-proc (cdadr exp) env))) (make-lambda parameters (begin check-proc (cddr exp))))))) (define (no-sub-list arg) (cond ((null? arg) #t) ((pair? (car arg)) #f) (else (no-sub-list (cdr arg))))) (define (find-parameters arg) (cond ((null? arg) '()) ((pair? (car arg)) (cons (cadr arg) (find-parameters (cdr arg)))) (else (cons (car arg) (find-parameters (cdr arg)))))) (define (find-check-proc arg env) (let ((one (car arg)) (rest (cdr arg))) (cond ((and (null? rest) (pair? one)) (make-check-lambda (cadr one) (car one) env)) ((pair? one) (make-lambda (cadr one) (begin (make-check-lambda (cadr one) (car one) env) (find-check-proc (cdr arg) env)))) (else (find-check-proc (cdr arg)))))) (define (make-check-lambda parameter body) (make-lambda parameter (if (not ((eval body env) parameter)) (error "Error: wrong argument type -- " parameter))))