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))