sicp 笔记 (10)

第四章习题 4.1 - 4.15 的解答。从本章开始使用 mit-scheme 9.1.1。

E-4.1: 题目的意思是,函数 list-of-values 对参数列表的求值顺序依赖于解析器的实现。如果解析器对参数列表的求值顺序是从右往左的,那么 list-of-values 的求值顺序也是从右往左的,也就是说,对于 list-of-values 中的“(cons ...)”,会先算“(list-of-values (rest-operands ...))”这部分,后算“(eval (first-operand ...))”,也就是从右往左了;如果解析器的求值顺序是从左往右,那么 list-of-values 的求值顺序也是从左往右。

(define (list-of-values-lr exps env) ; evaluates from left to right
  (if (no-operands? exps)
    '()
    (let ((first-value (eval (first-operand exps) env)))
      (cons first-value
            (list-of-values (rest-operands exps) env)))))

(define (list-of-values-rl exps env) ; evaluates from right to left
  (if (no-operands? exps)
    '()
    (let ((rest-exp (rest-operands exps)))
      (cons (eval (first-operand exps) env)
            (list-of-values rest-exp env)))))

E-4.2: (a) 如果先判断是否 application?,那么“(define x 3)”会被当成是函数来执行,结果会出错。 (b) 把 application? 的定义修改为

(define (application? expression) (tagged-list? expression 'call))

其它几个和 application? 相关的函数也要修改。

E-4.3:

(define (my-eval expression env)
  (cond
    ((self-evaluating? expression)
     expression)
    (else
      ((get 'my-eval (operator expression)) (operands expression) env))))

E-4.4:

(define (and? expression)
  (tagged-list? expression 'and))

(define (eval-and expression env)
  (define (and-true? arglist)
    (if (no-operands? arglist)
      #t
      (and (my-eval (first-operand arglist) env)
           (and-true? (rest-operands arglist)))))

  (and-true? (cdr expression)))

(define (or? expression)
  (tagged-list? expression 'or))

(define (eval-or expression env)
  (define (or-true? arglist)
    (if (no-operands? arglist)
      #f
      (or (first-operand arglist)
          (eval-or (rest-operands arglist) env))))

  (or-true? (cdr expression)))

E-4.5: 先判断是否扩展形式,如果是的话就直接解析,否则转成 if 的形式。

(define (cond=>? expression)
  (let ((first (car expression))
        (rest (cdr expression)))
    (if (null? rest)
      #f
      (if (pair? rest)
        (if (eq? (car rest) '=>)
          #t
          #f)
        #f))))

(define (eval-cond=> expression env)
  (let ((result (my-eval (car expression) env))
        (recipient (cddr expression)))
    (if result
      (my-eval (recipient result) env))))

(define (eval-cond expression env)
  (if (cond=>? expression)
    (eval-cond=> expression env)
    (my-eval (cond->if expression))))

E-4.6:

(load "4.1-the.metacircular.evaluator.scm")

(define (let->combination expression)
  (let ((let-list (cadr expression))
        (let-body (cddr expression)))
    (let ((var-list (map car let-list))
          (exp-list (map cadr let-list)))
      (cons (make-lambda var-list let-body)
            exp-list))))

; ------------------------------------------------------------

(define str '(let ((a 1) (b 2) (c 3))
               (+ a b)))

(let->combination str)
</pre>

E-4.7: 题目中的定义转化为下面的形式:

<pre lang='scheme' line='1'>
(let ((x 3)) 
  (let ((y (+ x 2)))
    (let ((z (+ x y 5)))
      (* x z))))

转换程序为:

(define (let*->nested-lets expression)

  (let ((let*-list (cadr expression))
        (let*-body (cddr expression)))

    (define (make-nested-lets var-list)
      (if (null? (cdr var-list))
        (append (list 'let)
                (list (list (car var-list)))
                let*-body)
        (list 'let
              (list (car var-list))
              (make-nested-lets (cdr var-list)))))

    (make-nested-lets let*-list)))

; -----------------------------------------------------------

(define str '(let* ((x 3)
                    (y (+ x 2))
                    (z (+ x y 5)))
               (* x z)
               (+ y z)))

(let*->nested-lets str)

转换后的结果可以使用E-4.6的程序验证。

E-4.8: 将题目中的定义变为以下形式(参考资料 [1]):

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

因此转换函数为:

(load "4.1-the.metacircular.evaluator.scm")

(define (named-let? expression)
  (and (pair? expression)
       (not (pair? (cadr expression)))))

(define (named-let->combination expression)
  (let* ((var-name (cadr expression))
         (let-list (caddr expression))
         (arg-list (map car let-list))
         (arg-init (map cadr let-list))
         (let-body (cdddr expression)))
    (list 'let
          (list (list var-name
                      (make-lambda arg-list let-body)))
          (cons var-name arg-init))))

; ------------------------------------------------------------

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

(named-let->combination str)

E-4.9: 跳过。

E-4.10: 随便选择一个语句(例如 assignment),修改其相关的函数定义即可。

E-4.11: 只实现了开头几个函数,后面几个接口改动都比较少,就不写了。

(define (make-frame variables values)
  (if (null? variables)
    '()
    (if (null? values)
      (append (list (list (car variables) #f))
              (make-frame (cdr variables) values))
      (append (list (list (car variables) (car values)))
              (make-frame (cdr variables) (cdr values))))))

(define (frame-variables frame)
  (map car frame))

(define (frame-values frame)
  (map cadr frame))

(define (add-binding-to-frame! var val frame)
  (let ((new-rest (map (lambda (x) x) frame)))
    (set-car! frame (list var val))
    (set-cdr! frame new-rest)))

; --------------------------------------------------------------

(make-frame (list 'a 'b 'c 'd) (list 1 2 3 4 5))
(make-frame (list 'a 'b 'c 'd) (list 1 2))

(define testframe (make-frame (list 'a 'b 'c 'd) (list 1 2 3 4 5)))

(frame-variables testframe)
(frame-values testframe)
(add-binding-to-frame! 'e 5 testframe)
(add-binding-to-frame! 'f 8 testframe)

E-4.12:

(load "4.1-the.metacircular.evaluator.scm")

(define (scan var frame eq-procedure)
  (define (recur vars vals)
    (cond ((null? vars) #f)
          ((eq? var (car vars))
           (eq-procedure vars vals))
          (else (recur (cdr vars) (cdr vals)))))

  (recur (frame-variables frame) (frame-values frame)))

(define (env-loop var env eq-procedure)
  (if (eq? env the-empty-environment)
    #f
    (let ((result (scan var (first-frame env) eq-procedure)))
      (if (eq? result #f)
        (env-loop var (enclosing-environment env) eq-procedure)
        result))))

(define (lookup-variable-value2 var env)
  (env-loop var env (lambda (vars vals) (car vals))))

(define (set-variable-value! var val env)
  (env-loop var env (lambda (vars vals) (set-car! vals val))))

(define (define-variable! var val env)
  (let ((frame (first-frame env)))
    (if (eq? (scan var frame (lambda (vars vals) (set-car! vals val))) #f)
      (add-binding-to-frame! var val frame))))

; -------------------------------------------------------------------------

(define testenv (list (make-frame '(d e f) '(10 20 30)) (make-frame '(a b c d) '(1 2 3 4))))

(lookup-variable-value2 'f testenv)
testenv
(set-variable-value! 'f 101 testenv)
testenv
(define-variable! 'x '100 testenv)
testenv

因为不知道如何将单个元素的“引用”传递给函数,只好将找到 var 之后的 vars 和 vals 都传给两个函数。

E-4.13: 这里的实现还有问题,当去掉最后的变量时会留下一个空的列表。

(load "4.12.scm")
(load "utils.scm")

(define (make-unbound! var env)
  (env-loop var env
            (lambda (vars vals)
              (remove-first! vars)
              (remove-first! vals)
              #t)))

; -------------------------------------------

testenv
(make-unbound! 'f testenv)
testenv

其中使用到的 remove-first! 函数定义在 utils.scm 中:

(define (remove-first! array)
  (if (not (null? array)) ; '(x)
    (if (not (null? (cdr array))) ; '(x y z)
      (begin (set-car! array (cadr array)) ; '(y y z)
             (if (null? (cddr array)) ; (y y)
               (set-cdr! array '()) ; '(y)
               (remove-first! (cdr array))))
      (set-car! array '()))))

(define (remove-first!))
(define (remove! element array)
  (define (recur rest)
    (if (null? rest)
      '()
      (if (eq? element (car rest))
        (remove-first! rest)
        (recur (cdr rest)))))

  (recur array))

E-4.14: 如果使用系统的 map,传给 map 的函数参数使用的是解析器定义的 procedure 格式(以“procedure”开始的list),因此 map 会报错。

E-4.15: 执行“(try try)”,如果 try 可以停的话(也就是说“(halts? try try)”为真),就会执行“(run-forever)”,结果是不会停的;如果“(halts? try try)”为假,最后“(try try)”却停了(输出了“halted”)。这样就产生悖论了。

参考资料

[1] 练习 4.8

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注