Let expressions are derived expressions, because
(let ((<var1> <exp1>) ... (<varn> <expn>))
<body>)
is equivalent to
((lambda (<var1> ... <varn>)
<body>)
<exp1>
<expn>)
Implement a syntactic transformation let->combination that reduces evaluating let expressions to evaluating combinations of the type shown above, and add the appropriate clause to eval to handle let expressions.
let 表达式使用下面的转换就可以变成 lambda,作为 derived expression 加入到解释器中。
值得注意的是,lambda 的 body 部分是可以使用多语句的。所以我们的 make-let 的 body 部分必须使用一个 list 来表示语句。
(define (install-eval-let)
;; import make-lambda
(define (make-lambda p b)
((get 'constructor lambda-tag) p b))
;; export constructor
;; NOTE: let's body passed in should be a sequence,
;; which means it should be a list!
(define (make-let vars vals body)
(append (list 'let
(map (lambda (x y) (list x y)) vars vals))
body))
(define (let-bindings exp)
(car exp))
(define (let-body exp)
(cdr exp))
(define (let-vars bindings)
(map (lambda (x) (car x)) bindings))
(define (let-vals bindings)
(map (lambda (x) (cadr x)) bindings))
(define (let->combination exp)
(cons (make-lambda (let-vars (let-bindings exp))
(let-body exp))
(let-vals (let-bindings exp))))
(define (eval-let exp env)
(eval (let->combination exp) env))
(put 'eval 'let eval-let)
(put 'constructor 'let make-let))
我们的测试用例需要确保 let 只是创造了 lambda 表达式而非去改变 environment:
; test the transformation
(let ((test-env (setup-environment)))
(begin
(install-eval-let)
(assert= (eval '(let ((x 1) (y 1))
(+ x y)) test-env)
2)
(assert= (eval '(let ((x 1))
(let ((x 2))
(+ x 2))) test-env)
4)
(assert= (eval '(let ((x 1))
(let ((x 2))
(set! x 3))
x) test-env)
1)
))