求值的环境模形

  1. 如 3.1.3 节所言,由于赋值的存在,变量已经不能再看作仅仅是某个值的名字,此 时的一个变量必须以某种方式指定了一个“位置”,相应的值可以存储再那里,在新求 值模形中,这种位置将维持在称为 环境 的结构种;
  2. 一个环境就是 框架(frame) 的一个序列,每个框架是包含着一些 约束 的一个 表格(可能为空),这些约束将一些变量名关联于对应的值(在一个框架里,任何变 量至多只能有一个约束);
  3. 每个框架还包含着一个指针,指向这一框架的外围环境,如果将相应的框架看作是全 局的,那么它将没有外围环境;
  4. 一个变量相对于某个特定环境的值,也就是在这一环境中,包含着该变量的第一个框 架里这个变量的约束值,如果在框架序列中并不存在这一变量的约束,那么我们就说 这个变量在该特定环境中是无约束的;
  5. 环境对于求值过程是至关重要的,因为它确定了表达式求值的上下文,在现在讨论的 求值模形中,我们将总说某个表达式相对于相对于某个环境的求值;
  6. 为了描述与解释器的交互作用,我们将始终假定存在着一个全局环境,它只包含着一 个框架(没有外围环境),这个环境里包含着所有关联于基本过程的符号的值;

求值规则

  1. 在求值的环境模形里,一个过程总是一个序对,由一些代码和一个指向环境的指针组成;
  2. 过程只能通过一种方式创建,那就是通过求值一个 lambda 表达式;
  3. 过程定义的语法形式,不过是作为其基础的隐含 lambda 表达式的语法糖衣;
  4. 在将一个过程应用于一组实际参数时,将会建立起一个新环境,其中包含了将所有 形式参数约束于对应的实际参数的框架,该框架的外围环境就是所用的那个过程的 环境,随后就在这个新环境之下求值过程的体;
  5. 相对于一个给定环境求值一个 lambda 表达式,将创建起一个过程对象,这个过 程对象是一个序对,由该 lambda 表达式的正文和指向环境的指针组成,这一指 针指向的就是创建这个过程对象时的环境;
  6. 在某个环境里求值表达式 (set! <variable> <value>), 要求我们首先在环境中 确定有关变量的约束位置,而后再修改这个约束,使之表示这个新值,这也就是说, 首先需要找到第一个包含这个变量的约束的框架,而后修改这一框架,如果该变量 在环境中没有约束, set! 将报告一个错误;

简单过程的应用

  1. 将一个过程应用于一组实际的参数,建立起新环境的机制,可以保证在各个新环境 中形参对应于实参的变量约束互不影响;

练习 3.9

1
2
3
4
;; 图就不画了;

;; 注意,如果只是基于目前的对于环境模形的认识,迭代版本的程序将会创建正比于 n 的
;; 环境个数,而不是一个,如 P167 脚注142 所言,这是有背于书本前面的断言的。

将框架看作局部状态的展台

  1. 环境起着保存过程对象的局部状态变量的“位置”的作用;

练习 3.10

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
;; 在全局环境过程定义 make-withdraw 后产生的过程对象具有 make-withdraw 的代码正
;; 文以及一个指向全局环境的指针;

;; (define w1 (make-withdraw 100)) 如书本所言,将建立一个新环境(E1)建立 形参
;; initial-amount 到数值 100 的约束,此环境的外围环境是全局环境,并在这一环境中
;; 对 make-withdraw 的过程体求值;

;; make-withdraw 的过程体的 let 形式实际上就是 ((lambda (<var>) <body>) <exp>)
;; 的语法糖衣,求值这个复合表达式,通过求值运算符 lambda 表达式将会建立一个过程
;; 对象,该对象由此 lambda 对象的正文和一个指向 E1 的指针,因为这个 lambda 表达
;; 式是在 E1 中求值的;

;; 使用这个匿名过程应用于参数,将会建立起一个新环境 (E2) 建立形参 balance 到实参
;; initial-amount 值的约束,此环境的外围环境是 E1, 并在这一环境中对匿名过程的体
;; 进行求值,此过程体是一个 lambda 表达式,对其求值得到一个过程对象,由 lambda
;; 表达式的代码部分和一个指向 E2 的指针组成,该过程对象作为返回值在最初的定义中
;; 与全局环境中的 w1 建立约束;

;; 所以调用 w1 将会建立起一个新环境,此环境的外围环境由 w1 所约束的过程对象指定
;; 即为 E2, 在新环境中求值 w1 过程对象的过程体时,使用的 balance 变量即是外围环
;; 境 E2 中的变量 balance.

;; 综上,两个版本创建出的过程对象具有相同的行为,因为过程体是一样的,作为过程体
;; 的局部状态变量的值是一样的,只不过第二种形式中多了一层环境。

内部定义

  1. 局部过程的名字不会与包容它们的过程之外的名字互相干扰,这是因为这些局部过程 名都是在该过程运行时创建的框架里面约束的,而不是在全局环境里约束的;
  2. 局部过程只需将包含着它们的过程的形参作为自由变量,就可以访问该过程的实际参 数,这是因为对于局部过程体的求值所在的环境是外围过程体求值所在的环境的下属;

练习 3.11

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
;; 画图就省了;

;; acc 的局部状态保存在 make-account 所关联的过程对象的过程体求值时创建的环境中;

;; 以同样的方式建立另一个帐户,将会把此帐户的局部状态变量保存在以同样方式创建的
;; 新环境中;

;; 两个环境中的 balance 变量约束虽然同名但互不干扰;

;; acc 和 acc2 共享的也就是各过程对象的代码正文部分了以及全局环境;