SICP笔记(七)

赋值和局部状态

现实中,我们常将世界看成聚集在一起的许多独立个体,每个个体都有自己的状态,比如你在夏日下搬砖,同时有人在宝马里吹空调,个体的状态随着时间不断改变,过去的改变会影响现在的表现,你辛辛苦苦搬了几个月砖,终于在年前开开心心地骑上小三轮回家过年。在计算机中,我们用状态变量(state variable)来记录对象的状态,系统中的独立对象通过自己的局部状态变量描述自己的状态,随着时间的流逝,对象的状态在不断改变,我们需要一种技术可以实时更新状态变量的值,这时我们就需要赋值(assignment)操作。

使用set!操作我们就可以对某个变量实现赋值操作,引进赋值操作后,我们愉快着的同时带来了系列麻烦,比如摧毁了代换模型,时刻注意语句的先后顺序,提防side-effect bugs。
不使用任何赋值的程序设计称为函数式程序设计(functional programming),函数式编程可以看成对数学事实的表达,每次对程序给定相同的参数,只能得到一个相同的结果。与函数式对应的是命令式程序设计(imperative programming),命令式大量使用赋值操作。由于赋值操作,每次对一个程序给定相同的参数,得到的结果可能不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(define (cons x y)
(lambda (m)
(m x
y
(lambda (n) (set! x n))
(lambda (n) (set! y n)))))

(define (car x)
(x (lambda (a b sa sb) a)))

(define (cdr x)
(x (lambda (a b sa sb) b)))

(define (set-car! x y)
(x (lambda (a b sa sb) (sa y))))

(define (set-cdr! x y)
(x (lambda (a b sa sb) (sb y))))

以上是对丘奇方法的修改,使用set!定义了set-car!set-cdr!,这个过程中,没有使用任何数据,通过lambda表达式创建过程对象,“无中生有”构造了序对。

环境模型

原先的代换模型基于“变量名是值的名字”这一事实,因为引入了赋值,值和变量名并不是一一对应,代换模型便无法适用于赋值操作存在的情况。我们构建新的计算模型,环境模型(environment model),来解决这个问题。环境模型建立的是变量名和储存值的地址之间的关系,并且变量的值允许改变。
环境模型的使用可以总结为两条规则:

  • 一个程序对象被用于一组参数,是通过构建一个新框架来完成的。框架包含形式参数到调用中使用的实际参数的映射,而后将在构造起的这一环境的上下文中求值过程体。这个新框架的外围环境就是作为被应用的那个过程对象的一部分的环境。
  • 相对于某个特定的环境求值lambda表达式,将创建起一个过程对象,一个由lambda表达式的文本和一个指向创建这个过程对象时所在环境的指针构成的序对。

数据结构

引入了赋值操作set!,最基本的序对也扩展了能力,增加了变动函数(mutator),我们有能力定义变动的表结构。基于变动的表结构,我们定义了链表(linked list),双向链表(doubly linked list),队列(queue),双端队列(deque),表格(table)等数据结构。其中实现的二维表格就可以实现上一章提到的operation table的功能。
我们模拟了两个实例,数字电路和约束的传播,两个实例中使用了面向对象的思想,将系统分成一个个功能模块,我们在计算机中模拟其在现实中的特征。每个对象实例通过自己的局部状态变量维护自己的状态,在系统中,各个对象实例互不影响。


习题解答:传送门