SICP 全笔记

Exercise 4.8. ‘‘Named let’’ is a variant of let that has the form

(let <var> <bindings> <body>)

The <bindings> and <body> are just as in ordinary let, except that <var> is bound within <body> to a procedure whose body is <body> and whose parameters are the variables in the <bindings>. Thus, one can repeatedly execute the <body> by invoking the procedure named <var>. For example, the iterative Fibonacci procedure (section 1.2.2) can be rewritten using named let as follows:

(define (fib n)
  (let fib-iter ((a 1)
                 (b 0)
                 (count n))
    (if (= count 0)
        b
        (fib-iter (+ a b) a (- count 1)))))

Modify let->combination of exercise 4.6 to also support named let.

named-let 也只需要借助于当前已经有的 let 表达式来完成定义。题目中的一个 named-let 的定义

(define (fib n)
  (let fib-iter ((a 1)
                 (b 0)
                 (count n))
    (if (= count 0)
        b
        (fib-iter (+ a b) a (- count 1)))))

转化为 let 的表达式应该为

(let ((fib-iter 1))
  (begin
    (set! fib-iter (lambda (a b count)
                     (if (= count 0)
                         b
                         (fib-iter (+ a b) a (- count 1)))))
    (fib-iter 1 0 n)))

我们先使用 let 表达式使 fib-iter 这个名称对于之后的内容可见–至于它的初始内容,就无关紧要了。在接下来要做的事情中,就是把这个名称与其要绑定的过程绑定,在绑定之后,过程中如果使用了这个名称来进行递归也没有问题了,因为当运行到这个递归语句的时候,我们的名称已经绑定到了相应过程上了。

这是一种让过程有递归能力的普遍方法:

  1. 让名称可见;
  2. 将名称绑定到相应过程上。

我们将对 eval-let 的 package 进行修改。主要修改 let->combination 过程,如果传递进去的不是 named-let,那么之前的操作不变。如果是 named-let,那么 let->combination 将会把 named-let 转化为 let,然后再转化为 lambda 表达式。

我们的 let->combination 如下:

  (define (let->combination exp)
    (if (named-let? exp)
        (let->combination (cdr (named-let->let exp)))
        (cons (make-lambda (let-vars (let-bindings exp))
                           (let-body exp))
              (let-vals (let-bindings exp)))))

let->combination 中使用到了 named-let->let 的过程,如下:

  (define (named-let->let exp)
    (let ((var (car exp))
          (bindings (cadr exp))
          (body (cddr exp)))

      (let ((procedure (make-lambda (let-vars bindings)
                                    body)))
        (make-let (list var)
                  (list 1) ; arbitrary value!
                  ((get 'constructor sequence-tag)
                   (list (list 'set! var procedure)
                         (cons var (let-vals bindings))))))))

最后是我们的测试用例

; test the transformation

(let ((test-env (setup-environment)))
  (begin
    (install-eval-named-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)
    (eval '(define (fib n)
             (let fib-iter ((a 1)
                            (b 0)
                            (count n))
               (if (= count 0)
                   b
                   (fib-iter (+ a b) a (+ count -1))))) test-env)
    (assert= (eval '(fib 10) test-env) 55)))