Scheme 方案环境模型关闭问题
引入环境模型代替替代模型 在学习这一部分时,我做了以下测试:Scheme 方案环境模型关闭问题,scheme,eval,apply,sicp,Scheme,Eval,Apply,Sicp,引入环境模型代替替代模型 在学习这一部分时,我做了以下测试: (define a1 1) (define (f1) a1) (f1) ; return 1 (define (f2) (define a1 2) a1) (f2) ; return 2 (define (f3) (define a1 2) (f1)) (f3) ; return 1,not 2 最后一句话出乎我的意料 来自SICP的句子标记 通过构造一个框架,将过程的形式参数绑定到调用的参数,然后在构造的新环境的上下文中评估过程体
(define a1 1)
(define (f1) a1)
(f1) ; return 1
(define (f2) (define a1 2) a1)
(f2) ; return 2
(define (f3) (define a1 2) (f1))
(f3) ; return 1,not 2
最后一句话出乎我的意料
来自SICP的句子标记
通过构造一个框架,将过程的形式参数绑定到调用的参数,然后在构造的新环境的上下文中评估过程体,将过程对象应用到一组参数。新框架将应用的过程对象的环境部分作为其封闭环境
根据此规则,当调用f1
时,在f3
中创建一个新环境,其封闭环境是全局的,而不是调用f2
时创建的环境(调用E1
)。对我来说,E1
应该是封闭的环境
为了简洁起见,我画了两幅描绘环境的画
类似的C示例:
int a = 1;
void f1() {
printf("a = %d from f1\n", a);
}
void f2() {
int a = 2;
printf("a = %d from f2\n", a);
}
void f3() {
int a = 2;
f1();
}
int main() {
f2();
f3();
return 0;
}
print:
a = 2 from f2
a = 1 from f1
我有什么遗漏吗?或者为什么
Scheme
和C
中的f3
都打印1而不是2?这里棘手的部分是f2
中的内部定义
如果你尝试这个程序,它使用set代码>而不是定义
你会得到你期望的结果(为了清楚起见,我重命名了上一个
功能设置为f3
)
输出:
1
2
2
现在,您在原始程序中看不到这种行为的原因是
内部定义:
(define (f2) (define a1 2) a1)
内部定义将扩展到
(define (f2)
(letrec ((a1 2))
a1))
如果我们重命名变量,则为:
(define (f2)
(letrec ((a2 2))
a2))
因此,internal define将分配一个新变量,因此a1绑定到的原始位置不受影响,因此保持值1
注意:在REPL(顶层)中,使用define
定义一个已定义的变量相当于“set!”!。也就是说,顶级定义和内部定义是两个独立的概念。考虑到在环境模型中,当您定义函数时,实际上您获得了一个闭包,即一对(函数,用于计算函数自由变量的环境)。自由变量是函数中提到的一个变量,它不是参数或局部变量,因此,当函数实际执行时,在闭包的环境部分搜索它
因此,当您定义f1
时,返回的闭包将作为当前全局环境,其中a1
的值为1
在对f2
的定义中,您将a1
定义为2
。这是一个局部定义,因此在主体中,首先在局部环境中搜索a1
的值,然后找到2
在f3
的定义中,您再次将a1
定义为2
,并且该绑定再次出现在本地环境中,但您调用了f1
,这是一个闭包,在执行过程中,将根据f1
的定义搜索a1
的值(找到的值是在闭合构建时使用的环境中存在的值,即1
)
这种解释变量的方法称为静态绑定,与动态绑定相反,动态绑定的结果是2
请注意,C和Scheme都使用静态绑定,您的C示例正好显示了这一点:调用f3
的结果是1
,因为这是打印在f1
中的值。另一方面,图像是不正确的。您应该将环境视为一组帧,每个帧都包含绑定(即耦合变量、当前值),连接到其他帧。因此,f1
、f2
、f3
的闭包不同
下图显示了全球环境的增长:
E1
是定义a1
之后的全局环境E2
在定义f1
之后,您可以注意到f1
的值是指向第二帧的闭包(这允许递归定义,因为f1
原则上可以调用自身)。在闭包中,a1
的值是E1
E3
中存在的值,是在定义了f2
之后的环境,闭包首先指向本地环境,a1
等于2
,然后指向当前的全局环境。最后,E4
是全局环境定义f3
后的环境。请注意,新闭包再次具有一个a2
等于2
的本地环境
调用f3
时,其主体内的f1
被检索为E2
中的f1
的值,当计算f1
时,则使用a1=1
另一方面,使用动态绑定,则无需创建闭包。每个函数都在当前环境中求值,当前环境是使用参数绑定和本地定义扩展的全局环境。因此,您可以想象全局环境现在只有以下形式:
但是当计算f3时,(定义a1 2)
会向环境中添加一个新的帧:
在这个环境中,对最后一个表单进行求值,(f1)
。在这个求值过程中,在环境中检索到f1
,并在相同的(唯一的)环境中再次对其主体进行求值,其中a2
在您的上述代码中有值2
,(define(f2)(letrec((a12))a2))
,a2
来自哪里?或者是一个打字错误,实际上是a1?对不起。有关内部定义的官方描述,请参阅R5RS中的本节:嗨,伦佐,我已经更新了C
示例
(define (f2)
(letrec ((a2 2))
a2))