虽说叫做副作用显得不太好听,但在Lisp中副作用还是非常重要的。而相对于所有状态都必须显式地操作和传递额外参数的方式,如果引进赋值和将状态隐藏在局部变量中,那么就可以用更加模块化的方式来构造系统。
正如你所知道的,不用任何赋值的程序设计称为函数式程序设计。相反,广泛采用赋值的程序设计称为命令式程序设计。在C等命令式程序设计语言中,我们往往都要仔细考虑变量赋值的顺序,尤其是在循环中,但在函数式程序设计中这类问题根本不会出现。
(define x 1)
(define (add-x y)
(set! x (1+ x))
(+ x y))
(add-x 3)
5
(add-x 3)
6
在这里我们看到同一个表达式却会导致两种不同的结果,而这一切都取决于时间。
我们常用define来定义一个为定义的变量、函数等等,而不用其来做修改的操作。set!则来完成修改这一工作。经常用到的let实际上是一个过程调用的语法糖衣。
(let ((var1 e1) (var2 e2))
e3)=>
((lambda (var1 var2)
e3)
e1
e2)
在前面的代换模型中,我们是这样求组合式的:
1)求值该组合式的各个子表达式
2)将作为最左子表达式(运算符)的值的那个过程应用于相应的实际参数,所谓实际参数也就是其他子表达式(运算对象)的值。
而在环境模型中,第二步就有所差异了:
1)求值该组合式的各个子表达式
2)将运算符子表达式的值应用于运算对象子表达式的值。
在将一个过程应用于一组实际参数时,将会建立起一个新环境,其中包含了将所有形式参数约束与对应的实际参数的框架,该框架的外围环境就是所用的那个过程的环境。
书中这部分可谓归纳的非常好,过程应用的环境模型的两条规则:
1)将一个过程对象应用于一集实际参数,将构造出一个新框架,其中将过程的形式参数约束到调用时的实际参数,而后在构造起的这一新环境的上下文中求值过程体。这个新框架的外围环境就是作为被应用的那个过程对象的一部分的环境。
2)相对于一个给定环境求值一个lambda表达式,将创建起一个过程对象,这个过程对象是一个序对,由该lambda表达式的正文和一个指向环境的指针组成,这一指针指向的就是创建这个过程对象时的环境。
用define定义一个符号,也就是在当前环境框架里建立一个约束,并赋予这个符号指定的值。