sicp 4.2.2小节部分习题

4.27,

;;; L-Eval input:
(define count 0)

;;; L-Eval value:
ok

;;; L-Eval input:
(define (id x)
  (set! count (+ 1 count))
  x)

;;; L-Eval value:
ok

;;; L-Eval input:
(define w (id (id 10)))

;;; L-Eval value:
ok

;;; L-Eval input:
count

;;; L-Eval value:
1

;;; L-Eval input:
w

;;; L-Eval value:
10

;;; L-Eval input:
count

;;; L-Eval value:
2

至于原因,w在没有强迫求值前,仅仅执行了一步(id 10),因此此时count为1,当要求打印w的时候force执行了第二步(id 10),因此count增加为2。

4.28,当参数也是函数的时候,例如:

(define square (lambda(x) (* x x)))
(define (test proc a)
  (proc a))
(test square 3)

如果对operator不采用actual-value,那么square将延时求值,在执行(proc a)时无法辨认eval的过程类型。

4.29,俺第一个想到的就是树形递归的斐波那契数列:

(define (fib n)
  (cond ((= 0 n) 0)
            ((= 1 n) 1)
            (else
              (+ (fib (- n 1)) (fib (- n 2))))))

不带记忆功能和带记忆功能的force-it之间的性能差距非常明显。

第二问,有趣的地方在于square过程,注意到(define (square x) (* x x)),x在body出现了两次,那么如果是使用不带记忆功能的force-it, x将被求值两次,如果x本身带有副作用(例如例子里面的id过程),那么显然副作用也将被调用两次,因此答案不言自明。带记忆功能的force-it版本中,count将仍然是1,而在不带记忆功能的版本中count将增长到2。

4.30,第一问,我也谈不出所以然为什么ben的说法是正确的,关注下第二问的两个过程在不同eval-sequence下的表现,(p1 1)的结果没有改变都是(1 2),而(p2 1)在原始版本的eval-sequence中结果是1,而在Cy修改后的版本中(对中间步骤采用actual-value)结果是(1 2),也就是说在原始版本中的(set! x (cons x '(2)))的副作用根本没有实现,而在修改后的版本中实现了。俺觉的这个问题很迷惑,惰性求值与side effect相互作用很奇特,不过我更偏向原始版本,因为我觉的这样的实现更容易看清代码的意图,也就是说在透明性上更好,例如我分析p2过程就可以认为直接返回参数x;而实现副作用很容易让人掉入陷阱,并且很可能引进难以查找的bug。
文章转自庄周梦蝶  ,原文发布时间2008-11-02

时间: 2024-12-02 23:22:57

sicp 4.2.2小节部分习题的相关文章

sicp 4.4.1小节习题

本节开始进入第4章最后一部分--逻辑程序设计.scheme将实现一种查询语言,非常类似prolog.由于解释器的实现在后面,还未读到,前面的习题我都将用prolog做测试,当然也给出scheme版本的解答,待以后测试.     首先给出依照书中所述写出的prolog事实库: address('BitDiddle Ben','Slumerville','Ridge Road',10). address('Hacker Alyssa P','Cambridge','Mass Ave',78). ad

sicp 3.1.1小节习题尝试解答

这一节主要是介绍局部状态变量,介绍了set!和begin的语法,看来ruby使用!号来表示改变变量值不是什么新鲜主意.    习题3.1,不解释了 ;习题3.1 (define (make-accumulator init)   (define (accumulator num)     (set! init (+ init num))     init)   accumulator) 习题3.2,非常有趣的例子,在内部维持一个计数的变量即可,如果传入的参数是特定的符号就返回计数或者清0,如果不

sicp 4.3.3小节习题

  本节实现了amb求值器,题目都是扩展这个求值器,引入一些特殊的过程.我的尝试解答从4.51开始 习题4.51,要求实现permanent-set!,这个过程的副作用在遇到失败时不撤销,实现如下: ;扩充analyze  ((permanent-assignment? exp)          (analyze-permanent-assignment exp)) ;实现 (define (permanent-assignment? exp)   (tagged-list? exp 'per

sicp 4.3.1小节两题

本节开始介绍神奇的amb函数,为引入逻辑程序设计做铺垫.关于amb,有清华王垠的一个文档: http://cs2.swfc.edu.cn/~wanghuan/wangyin1/amb/amb.html 4.35,与an-element-of类似: (define (an-integer-between low high)   (require (not (> low high)))   (amb low (an-integer-between (+ low 1) high))) 4.36,与练习

sicp 习题3.6-3.8试解

习题3.6,我的实现如下: (define rand   (let ((x 3))      (lambda(arg)       (cond((eq? arg 'generate)             ((lambda()(set! x (rand-update x)) x)))            ((eq? arg 'reset)             (lambda(init) (set! x init) (set! x (rand-update x)) x))         

sicp 2.3小结习题尝试解答

 习题2.2没有全部做,我读书的速度远远超过做习题的进度,没办法,时间有限,晚上的时间基本用来看书了,习题也都是在工作间隙做的,慢慢来了,前两章读完再总结下.回到2.3节,这一节在前几节介绍数值型符号数据的基础上引入了符号数据,将任意符号作为数据的能力非常有趣,并给出了一个符号求导的例子,实在是太漂亮了. 习题2.53,直接看结果: > (list 'a 'b 'c)(a b c)> (list (list 'george))((george))> (cdr '((x1 x2) (y1 

sicp习题2.33-2.39尝试解答

这一节的内容非常有趣,通过将序列作为interface,在此基础上进而提取出各种高阶操作(map,filter,accumulate,enumerate等),由此引出模块化设计的讨论.模块化设计带来复杂性的降低,同时可能引入性能上的损失,比如书中对sum-odd-squares过程的两种写法,原来的写法枚举列表元素的过程散落在累积.过滤.映射的过程中,主要一次循环就够了,而通过三个高阶过程来操作反而需要3次的遍历. 习题2.33,将map,append,length基本过程用累积操作重新定义,联

sicp 5.1节习题尝试解答

5.1 图就不画在机器上了,麻烦 5.2 用寄存器语言描述5.1题中的阶乘机器,加上了读取和打印,这里的解答全部在实际的寄存机器中验证过,但是仍然按照该节的表示法表示. (controller   fac-loop    (assign n (op read))    (assign product (const 1))    (assign counter (const 1))   iter-loop    (test (op >) (reg counter) (reg n))    (bra

sicp 3.12 3.13 3.14习题解答

习题3.12,append不是改变函数,不会改变x的结构.append!是改变函数,显然第一个response是(b),第二个就是(b c d).图就不画了,画了一次太麻烦了. 习题3.13,利用set-cdr!形成环,比较有意思: (define z (make-cycle (list 'a 'b 'c 'd))) z在DrScheme上表示为: #0=(a b c d . #0#) 形象地展示了一个环,显然运行(last-pair z)将陷入无限递归,因为(null? (cdr x))永远不