计算机程序的构造和解释(SICP) 第4章 习题解析 Part3
这次回顾第四章第三部分习题。
学习资料:
https://github.com/DeathKing/Learning-SICP
https://mitpress.mit.edu/sites/default/files/sicp/index.html
https://www.bilibili.com/video/BV1Xx41117tr?from=search&seid=14983483066585274454
参考资料:
https://sicp.readthedocs.io/en/latest
http://community.schemewiki.org/?SICP-Solutions
http://community.schemewiki.org/?sicp
https://liujiacai.net/blog/2016/02/22/recursion-without-name/
https://mvanier.livejournal.com/2897.html
https://github.com/jiacai2050/sicp
4.22(p276)
代码如下:
(load "ch4-mceval.scm")
(define (let? exp) (tagged-list? exp 'let))
(define (let-var-exp exp) (cadr exp))
(define (let-body exp) (cddr exp))
(define (get-var varexp)
(if (null? varexp)
'()
(cons (caar varexp) (get-var (cdr varexp)))))
(define (get-exp varexp)
(if (null? varexp)
'()
(cons (cadar varexp) (get-exp (cdr varexp)))))
(define (analyze-let->combination exp)
(let ((varlist (get-var (let-var-exp exp)))
; add
(explist (map analyze (get-exp (let-var-exp exp))))
(body (analyze (let-body exp))))
(cons (make-lambda varlist body) explist)))
(define (analyze exp)
; add
(cond ((let? exp) (analyze-let->combination exp))
(else
(error "Unknown expression type -- EVAL" exp))))
4.23(p276)
参考资料:
http://community.schemewiki.org/?sicp-ex-4.23
Alyssa方法没有对内部的表达式执行分析,具体例子如下:
(define exps (exp))
(define procs (map analyze exps)) = (res)
; 课本方法
(loop (car procs) (cdr procs))
= (lambda (env) (res env))
; Alyssa方法
(lambda (env) (execute-sequence procs env))
4.24(p276)
略过,后续补充。
4.25(p278)
应用序会一直展开,然后递归爆栈,例子如下:
(define (unless condition usual-value exceptional-value)
(if condition exceptional-value usual-value))
(define (factorial n)
(display n)
(unless (= n 1)
(* n (factorial (- n 1)))
1))
(factorial 5)
; 调用过程
; (* 5 (factorial 4))
; (* 5 (* 4 (factorial 3)))
; (* 5 (* 4 (* 3 (factorial 2))))
; (* 5 (* 4 (* 3 (* 2 (factorial 1)))))
; (* 5 (* 4 (* 3 (* 2 (* 1 (factorial 0))))))
; ...
4.26(p278)
参考资料:
https://github.com/jiacai2050/sicp/blob/master/exercises/04/4.26.md
http://community.schemewiki.org/?sicp-ex-4.26
特殊形式可以自己指定是否使用惰性求值,但是过程则不可以。
代码参考了第二份参考资料:
(load "ch4-mceval.scm")
; 派生表达式
(define (unless condition usual-value exceptional-value)
(if condition exceptional-value usual-value))
(define (unless? exp) (tagged-list? exp 'unless))
(define (unless-condition exp) (cadr exp))
(define (unless-usual-value exp) (caddr exp))
(define (unless-exceptional-value exp) (cadddr exp))
(define (unless->if exp)
(make-if (unless-condition exp)
(unless-usual-value exp)
(unless-exceptional-value exp)))
; 高阶
(define (test f pred a b c)
(f (pred a b c)))
(test unless a b c)
4.27(p282)
调用过程:
count
; 计算了(id 10), 所以为1
w
; 计算了(id (id 10)), 所以为10
count
; 计算了(id (id 10)), 所以为2
代码:
(load "ch4-leval.scm")
(define the-global-environment (setup-environment))
(driver-loop)
(define (try a b)
(if (= a 0) 1 b))
(try 0 (/ 1 0))
(define count 0)
(define (id x)
(set! count (+ count 1))
x)
(define w (id (id 10)))
count
; 0
w
; 10
count
; 2
结果:
;;; L-Eval input:
count
;;; L-Eval value:
1
;;; L-Eval input:
; 0
w
id
+
;;; L-Eval value:
10
;;; L-Eval input:
; 10
count
;;; L-Eval value:
2
4.28(p282)
参考资料:
http://community.schemewiki.org/?sicp-ex-4.28
例子:
(load "ch4-leval.scm")
(define the-global-environment (setup-environment))
(driver-loop)
(define (a x)
(+ x 1))
(define (b a x)
(a x))
(b a 1)
如果直接使用eval,则没有对a进行求值,调用会出错。
4.29(p282)
代码如下:
(load "ch4-leval.scm")
(define the-global-environment (setup-environment))
(driver-loop)
(define count 0)
(define (id x)
(set! count (+ count 1))
x)
(define (square x)
(* x x))
(square (id 10))
(display count)
有记忆:
(square (id 10)) : 100
count : 1
无记忆:
(square (id 10)) : 100
; (* (id 10) (id 10))
count : 2
4.30(p282)
(a)
因为使用了display,display是primitive-procedure,根据如下代码,会直接执行:
((primitive-procedure? procedure)
(apply-primitive-procedure
procedure
(list-of-arg-values arguments env)))
(b)
正文方法:
(p1 1) : (1 2)
(p2 1) : 1
其中第二项为1是因为无需执行
(set! x (cons x '(2)))
Cy的方法:
(p1 1) : (1 2)
(p2 1) : (1 2)
(c)
如果调用了primitive-procedure,那么肯定会求出值。
(d)
我觉得正文的方法好,效率高,而且是惰性求值。
4.31(p283)
参考资料:
http://community.schemewiki.org/?sicp-ex-4.31
代码如下:
(load "adaptive-eval.scm")
(define the-global-environment (setup-environment))
(driver-loop)
; test
; 基本过程
(define a 1)
(+ a 1)
; 常规过程
(define (f x)
(+ x 2))
(f 3)
; lazy
(define (try a (b lazy))
(if (= a 0) 1 b))
(try 0 (/ 1 0))
; lazy vs lazy memo
; lazy
(define (square x)
(* x x))
(define count1 0)
(define (id1 (x lazy))
(set! count1 (+ count1 1))
x)
(define w1 (id1 (id1 10)))
(display count1)
; 0
w1
; 10
(display count1)
; 2
; lazy memo
(define count2 0)
(define (id2 (x lazy-memo))
(set! count2 (+ count2 1))
x)
(define w2 (id2 (id1 10)))
(display count2)
; 0
w2
; 10
(display count2)
; 1
adaptive-eval.scm代码:
(load "ch4-mceval.scm")
(define input-prompt ";;; L-Eval input:")
(define output-prompt ";;; L-Eval value:")
(define (driver-loop)
(prompt-for-input input-prompt)
(let ((input (read)))
(let ((output
(actual-value input the-global-environment)))
(announce-output output-prompt)
(user-print output)))
(driver-loop))
; add start
(define (no-lazy? exp)
(not (pair? exp)))
(define (lazy? exp)
(eq? (cadr exp) 'lazy))
(define (lazy-memo? exp)
(eq? (cadr exp) 'lazy-memo))
(define (thunk? obj) (tagged-list? obj 'thunk))
(define (thunk-memo? obj) (tagged-list? obj 'thunk-memo))
; add end
(define (eval exp env)
(cond ((self-evaluating? exp) exp)
((variable? exp) (lookup-variable-value exp env))
((quoted? exp) (text-of-quotation exp))
((assignment? exp) (eval-assignment exp env))
((definition? exp) (eval-definition exp env))
((if? exp) (eval-if exp env))
((lambda? exp)
(make-procedure (lambda-parameters exp)
(lambda-body exp)
env))
((begin? exp)
(eval-sequence (begin-actions exp) env))
((cond? exp) (eval (cond->if exp) env))
((application? exp) ; clause from book
(apply (eval (operator exp) env)
(operands exp)
env))
(else
(error "Unknown expression type -- EVAL" exp))))
(define (apply procedure arguments env)
(cond ((primitive-procedure? procedure)
(apply-primitive-procedure
procedure
; change
(list-of-values arguments env)))
((compound-procedure? procedure)
(eval-sequence
(procedure-body procedure)
(extend-environment
; change
(adaptive-procedure-parameters procedure)
; change
(list-of-args procedure arguments env)
(procedure-environment procedure))))
(else
(error
"Unknown procedure type -- APPLY" procedure))))
(define (actual-value exp env)
(force-it (eval exp env)))
; change
(define (force-it obj)
(cond ((thunk? obj) (actual-value (thunk-exp obj) (thunk-env obj)))
((thunk-memo? obj)
(let ((result (actual-value
(thunk-exp obj)
(thunk-env obj))))
(set-car! obj 'evaluated-thunk)
(set-car! (cdr obj) result) ; replace exp with its value
(set-cdr! (cdr obj) '()) ; forget unneeded env
result))
((evaluated-thunk? obj)
(thunk-value obj))
(else obj)))
; add start
(define (list-of-values exps env)
(if (no-operands? exps)
'()
(cons (eval (first-operand exps) env)
(list-of-values (rest-operands exps) env))))
; 获得参数
(define (adaptive-procedure-parameters procedure)
(define (get-args par-list)
(if (no-operands? par-list)
'()
(if (pair? (first-operand par-list))
(cons (car (first-operand par-list)) (get-args (rest-operands par-list)))
(cons (first-operand par-list) (get-args (rest-operands par-list))))))
(get-args (procedure-parameters procedure)))
(define (list-of-args procedure exps env)
(define (get-args par-list exps env)
(if (no-operands? exps)
'()
(cond ((no-lazy? (first-operand par-list))
(cons (actual-value (first-operand exps) env)
(get-args (rest-operands par-list) (rest-operands exps)
env)))
((lazy? (first-operand par-list))
(cons (delay-it (first-operand exps) env)
(get-args (rest-operands par-list) (rest-operands exps)
env)))
((lazy-memo? (first-operand par-list))
(cons (delay-it-memo (first-operand exps) env)
(get-args (rest-operands par-list) (rest-operands exps)
env)))
(else (error
"Unknown parameter type -- " (first-operand par-list))))))
(get-args (procedure-parameters procedure) exps env))
(define (delay-it exp env)
(list 'thunk exp env))
(define (delay-it-memo exp env)
(list 'thunk-memo exp env))
(define (thunk? obj)
(tagged-list? obj 'thunk))
(define (thunk-exp thunk) (cadr thunk))
(define (thunk-env thunk) (caddr thunk))
; add end
;; "thunk" that has been forced and is storing its (memoized) value
(define (evaluated-thunk? obj)
(tagged-list? obj 'evaluated-thunk))
(define (thunk-value evaluated-thunk) (cadr evaluated-thunk))
;; A longer list of primitives -- suitable for running everything in 4.2
;; Overrides the list in ch4-mceval.scm
(define primitive-procedures
(list (list 'car car)
(list 'cdr cdr)
(list 'cons cons)
(list 'null? null?)
(list 'list list)
(list '+ +)
(list '- -)
(list '* *)
(list '/ /)
(list '= =)
(list 'newline newline)
(list 'display display)
;; more primitives
))
'ADAPTIVE-LAZY-EVALUATOR-LOADED
输出:
;;; L-Eval input:
; test
; 基本过程
(define a 1)
;;; L-Eval value:
ok
;;; L-Eval input:
(+ a 1)
;;; L-Eval value:
2
;;; L-Eval input:
; 常规过程
(define (f x)
(+ x 2))
;;; L-Eval value:
ok
;;; L-Eval input:
(f 3)
;;; L-Eval value:
5
;;; L-Eval input:
; lazy
(define (try a (b lazy))
(if (= a 0) 1 b))
;;; L-Eval value:
ok
;;; L-Eval input:
(try 0 (/ 1 0))
;;; L-Eval value:
1
;;; L-Eval input:
; lazy vs lazy memo
; lazy
(define (square x)
(* x x))
;;; L-Eval value:
ok
;;; L-Eval input:
(define count1 0)
;;; L-Eval value:
ok
;;; L-Eval input:
(define (id1 (x lazy))
(set! count1 (+ count1 1))
x)
;;; L-Eval value:
ok
;;; L-Eval input:
(define w1 (id1 (id1 10)))
;;; L-Eval value:
ok
;;; L-Eval input:
(display count1)1
;;; L-Eval value:
#!unspecific
;;; L-Eval input:
; 0
w1
;;; L-Eval value:
10
;;; L-Eval input:
; 10
(display count1)2
;;; L-Eval value:
#!unspecific
;;; L-Eval input:
; 2
; lazy memo
(define count2 0)
;;; L-Eval value:
ok
;;; L-Eval input:
(define (id2 (x lazy-memo))
(set! count2 (+ count2 1))
x)
;;; L-Eval value:
ok
;;; L-Eval input:
(define w2 (id2 (id1 10)))
;;; L-Eval value:
ok
;;; L-Eval input:
(display count2)1
;;; L-Eval value:
#!unspecific
;;; L-Eval input:
; 0
w2
;;; L-Eval value:
10
;;; L-Eval input:
; 10
(display count2)1
;;; L-Eval value:
#!unspecific
;;; L-Eval input:
; 1