《计算机程序的构造与解释》(十一)

来源:互联网 发布:java节假日 编辑:程序博客网 时间:2024/05/01 21:28

第三章关于程序的模块化、对象和状态

局部状态

    前面介绍了过程抽象和数据抽象,通过这两种抽象已经可以完成较强的计算功能。并且程序的组织结构也显得非常的优雅、易扩展;抽象背后的实现也可以随时修改,只要保证抽象接口不变。有一种强有力的设计原则就是依据模拟的真实世界的对象,去设计程序的结构。也就是说,程序的计算对象与待模拟的真实物理对象有对应关系。

    前面说的过程定义,有输入参数和返回值,它们的重要特点是只要参数相同,返回值总是固定的。例如加法操作add,有表达式(add 1 2),它总是返回3,无论什么时候,在什么情况下上面的表达式的值都是固定的。但是有时候,甚至是绝大多数的时候,真实物理世界的对象是“有状态”的。对象的操作过程会改变这个状态,并且会影响这个操作的下一次调用的返回情况。例如一个人作为对象,他有吃饭(eat)的动作。在吃了一碗饭之后,他就感觉有点饱了,虽然他可能还以再吃,但是并不能一直吃,因为“饱的状态”是会积累的。这就是说这个函数(eat)有side effect。这个副作用是通过对象的中间状态保持的。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;(define (make-monitor sqrt)    (let ((count 0))       (lambda(fun)          (cond ((eq? fun 'how-many-call?) count)                ((eq? fun 'reset)                  (set! count 0) )                ((number? fun)                 (begin (set! count (+ count 1))                        (sqrt fun)))                (else "no requre demanded!")))))> (define s (make-monitor sqrt))> (s 1)1> (s 100)10> (s 'how-many-call?)2> (s 'reset)> (s 'how-many-call?)0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;> (define (make-acc acc)    (lambda(x)      (begin (set! acc (+ acc x))              acc)))> (define A (make-acc 5))> (A 1)6> (A 1)7> (A 1)8

上面举了两个例子,说明含有状态的过程,以及如何改变这个状态。第一个例子利用count,保存sqrt被调用的次数,而且还可以显示或者置零该计数器。如果所带的参数不是'reset和how-many-call?,而是数字,那么就求这个数的平方根,并把它作为整个过程的返回值。第二例子比较简单,acc是累加器,将参数累加到acc中,并作为整个过程的值。

在命令式语言中,如C、java,局部变量的赋值顺序将改变过程的执行结果,使得学习者不断考虑是变量的赋值顺序,以保障每个语句使用的是变量的正确版本。如果运行并发执行的语言,这个复杂性将大大增加。

同一与变化

变量和对象名称,与它们实际指代的东西(或者某种计算过程)相对应,我叫它们“名与实”。
同一个变量名,可能指向不同的对象;同样,不同的变量名称可能指代同一个对象。这里的“对象”是指实际所指的空间及其所存储的值或者抽象的过程,也就是“实”。在某一过程中定义一个与外部变量同名的局部变量,它们就指向不同的对象,但是名称相同。
(define peter-acc (make-account 100))(define palu-acc peter acc)
这里peter-acc和paul-acc虽然名称不同,但是它们都指向同一个对象,修改它们中的任何一个,另一个都可以看见。



0 0
原创粉丝点击