Scheme 的变形-惰性求值

  1. 发明新的语言,常常就是先用一种现有的高级程序设计语言写出一个嵌入了这个新语 言的求值器;

正则序和应用序

  1. 惰性求值的一个优点就是使某些过程能够完成有用的计算,即使对它们的某些参数 的求值将产生错误甚至根本不能终止;
  2. 在一个纯的应用序语言里,所有的过程相对于每个参数都是严格的;而在一个纯的 正则序语言里,所有的复合过程对每个参数都是非严格的,而基本过程可以是严格 的,也可以是非严格的;

练习 4.25-4.26

4.25

1
2
3
4
5
6
;; in applicative order scheme
;; this factorial would runs into death loop
;; even with the n equals to 1
;; the (* n (factorial (- n 1))) will be computed in applicative order

;; it will pass in normal order scheme

4.26

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
;; a

(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)
  (let ((condition (unless-condition exp))
	(usual-value (unless-usual-value exp))
	(usual-exceptional-value exp))
    (make-if condition usual-exceptional-value usual-value)))

(define (eval-unless exp env)
  (eval (unless-if exp) env))

;; implemetn unless? and eval-unless in eval

;; b
;; a case which is about high order procedure...

一个采用惰性求值的解释器

  1. 需要完成的所有修改都围绕着过程应用;

练习 4.27-4.31

4.27

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
(define count 0)

(define (id x)
  (set! count (+ count 1))
  x)

(define w (id (id 10)))

;; count
;; 1

;; w
;; 10

;; count
;; 2

4.28

1
2
3
4
5
;; the operator maybe a thunk lambda object
;; if not pass the actual-value result of operator to apply;
;; within apply it would always fall to the third clause
;; which reports error.
;; so it must be evaluated by actual-value before passed to apply.

4.29

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(define (fibonacci n)
  (cond ((= n 0) 0)
	((= n 1) 1)
	(else (+ (fibonacci (- n 1))
		 (fibonacci (- n 2))))))
;; better with memo-proc


(define (square x)
  (* x x))

;; with memo
;; (square (id 10))
;; 100

;; count
;; 1

;; without memo
;; (square (id 10)
;; 100

;; count
;; 2

4.30

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
;; a
;; because the procedure body of (lambda (x) (newline) (display x)) has two primitive procedures
;; when evaluated using eval, it will call actual-value to get x

;; b
;; the original one
;; (p1 1) => (1 2)
;; (p2 1) => 1 (set! x (cons x '(2))) would be delayed in (p (set! x (cons x '(2))))

;; cy's version
;; (p1 1) => (1 2)
;; (p2 1) => (1 2)

;; c
(define (actual-value exp env)
  (force-it (eval exp env)))

(define (force-it obj)
  (if (thunk? obj)
      (actual-value (thunk-exp obj) (thunk-env obj))
      obj))				;when the exp is not a thunk, just return it


;; define
;; I prefer cy's way.

4.31

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
;; in eval
((application? exp)
 (apply (actual-value (operator exp) env)
	(operands exp)
	env))				;keep same

(define (apply procedure arguments env)
  (cond ((primitive-procedure? procedure)
	 (apply-primitive-procedure
	  procedure
	  (list-of-arg-values arguments env)))
	((compound-procedure? procedure)
	 (let ((parameters (procedure-parameters procedure)))
	   (eval-sequence
	    (procedure-body procedure)
	    (extend-environment
	     (map (lambda (i) (if (not (pair? i)) i (car i))) parameters)
	     (list-of-mixed-args parameters arguments env)
	     (procedure-environment procedure)))))
	(else
	 (error
	  "Unknown procedure type -- APPLY" procedure))))

(define (list-of-arg-values exps env)
  (if (no-operands? exps)
      '()
      (cons (actual-value (first-operand exps) env)
	    (list-arg-values (rest-operands exps)
			     env))))

(define (list-of-mixed-args prms exps env)
  (define (on-prms prm exp env)
    (cond ((not (pair? prm)) (actual-value exp env))
	  ((eq? (cdr prm) 'lazy) (delay-it exp env))
	  ((eq? (cdr prm) 'lazy-memo) (delay-it-memo exp env))))
  (if (no-operands? exps)
      '()
      (cons (on-prms (car prms) (first-operand exps) env)
	    (list-of-mixed-args (cdr prms) (rest-operands exps) env))))

(define (delay-it exp env)
  (list 'thunk exp env))

(define (delay-it-memo exp env)
  (list 'thunk-memo exp env))

(define (actual-value exp env)
  (force-it (eval exp env)))

(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)
	   (set-cdr! (cdr obj) '())
	   result))
	((evaluated-thunk? obj)
	 (thunk-value obj))
	(else obj)))

;; other procedures kept unchanged

将流作为惰性的表

  1. 将序对表示为过程,而不是去扩充惰性求值器使 cons 变成非严格的基本过程,这 样可以充分利用求值器的惰性求值特性;

练习 4.32-4.34

4.32

1
2
3
4
5
6
7
8
9
;; car and cdr of lazy list in this chapter are both delayed
;; only cdr of lazy list in chapter 3 is delayed

;; how could one programmer makes advantage of this feature?
;; just like the procedure list-ref
;; only computes the desired car part
;; as for the version defined in chapter 3
;; every car part would be computed until n equals to index;
;; the one in chapter 4 text is more efficient

4.33

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
(define (quoted? exp)
  (tagged-list? exp 'quote))

(define (text-of-quotation exp)
  (let ((temp (cadr exp)))
    (if (not (pair? temp))
	temp
	(new-list temp))))

(define (new-list pair)
  (if (null? pair)
      '()
      (make-procedure
       '(m)
       (list (list 'm 'car-value 'cdr-value))
       (extend-environment
	(list 'car-value 'cdr-value)
	(list (car pair) (new-list (cdr pair)))
	the-empty-environment))))

4.34

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(define (user-print object)
  (cond ((compound-procedure? object)
	 (display (list 'compound-procedure
			(procedure-parameters object)
			(procedure-body object)
			'<procedure-env>)))
	((lazy-list? object)
	 (display (list 'lazy-list
			(caddddr object)
			'...)))
	(else (display object))))

;; reserve the old cons car cdr in evaluator

(define old-cons cons)

(define old-car car)

(define old-cdr cdr)

(define (tagged-list? exp symbol)
  (eq? (old-car exp) symbol))

(define (lazy-list? exp)
  (tagged-list? exp 'lazy-list))

(define (cons x y)
  (old-cons 'lazy-list (lambda (m) (m x y))))

(define (car z)
  ((old-cdr z) (lambda (p q) p)))

(define (cdr z)
  ((old-cdr z) (lambda (p q) q)))