越南涂山娱乐场
官网
Portraits
Journal
Contact
好了,我们终于到了最后的关头,函数调用。为了直观,我们把函数调用的代码拷贝如下:
[`(,e1 ,e2) (let ([v1 (interp e1 env)] ; 计算函数 e1 的值 [v2 (interp e2 env)]) ; 计算参数 e2 的值 (match v1 [(Closure `(lambda (,x) ,e) env-save) ; 用模式匹配的方式取出闭包里的各个子结构 (interp e (ext-env x v2 env-save))]))] ; 在闭包的环境env-save中把x绑定到v2,解释函数体 函数调用都是 (e1 e2) 这样的形式,e1 表示函数,e2 是它的参数。我们需要先分别求出函数 e1 和参数 e2 的值。
函数调用就像把一个电器的插头插进插座,使它开始运转。比如,当 (lambda (x) (* x 2)) 被作用于 1 时,我们把 x 绑定到 1,然后解释它的函数体 (* x 2)。但是这里有一个问题,函数体内的自由变量应该取什么值呢?从上面闭包的讨论,你已经知道了,自由变量的值,应该从闭包的环境查询。
操作数 e1 的值 v1 是一个闭包,它里面包含一个函数定义时保存的环境 env-save。我们把这个环境 env-save 取出来,那我们就可以查询它,得到函数体内自由变量的值。然而函数体内不仅有自由变量,还有对函数参数的使用,所以我们必须扩展这个 env-save 环境,把参数的值加进去。这就是为什么我们使用 (ext-env x v2 env-save),而不只是 env-save。
你可能会奇怪,那么解释器的环境 env 难道这里就不用了吗?是的。我们通过 env 来计算 e1 和 e2 的值,是因为 e1 和 e2 里面的变量,在“当前环境”(env)里面看得见。可是函数体的定义,在当前环境下是看不见的。它的代码在别的地方,而那个地方看得见的环境,被我们存在闭包里了,它就是 env-save。所以我们把 v1 里面的闭包环境 env-save 取出来,用于计算函数体的值。
有意思的是,如果我们用 env,而不是env-save 来解释函数体,那我们的语言就变成了 dynamic scoping。现在来实验一下:你可以把 (interp e (ext-env x v2 env-save)) 里面的 env-save 改成 env,再试试我们之前讨论过的代码,它的输出就会变成 12。那就是我们之前讲过的,dynamic scoping 的结果。
(r2
官网
Portraits
Journal
Contact