SICP 3.2引入环境模型来替换替代模型。
我在学习这部分时做了以下测试:
(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
时,会创建一个新环境,其封闭环境是全局的,而不是在调用时创建的环境(调用E1
} f2
1}}。对我来说,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
我有什么遗漏的吗?或为什么f3
和Scheme
中的C
打印1而不是2?
答案 0 :(得分:3)
考虑到在环境模型中,当您定义一个函数时,实际上您获得了一个闭包,这是一对(函数,环境,用于评估 free 函数的变量)。自由变量值是函数中提到的变量,它不是参数或局部变量,因此,当实际执行函数时,它将在闭包的环境部分中进行搜索。
因此,当您定义f1
时,返回的闭包具有当前全局环境作为环境,其中a1
具有值1
。
在f2
的定义中,您将a1
定义为2
。这是本地定义,因此在正文中,首先在本地环境中搜索a1
的值,然后找到2
。
在f3
的定义中,您再次将a1
定义为2
,并且此绑定再次出现在本地环境中,但您调用f1
,即a1
一个闭包,在其执行期间,根据f1
的定义搜索1
的值(找到的值是在闭包建筑中使用的环境中出现的值)时间,即2
。)
这种解释变量的方式称为静态绑定,与动态绑定形成对比,其中结果为f3
。
请注意,C和Scheme都使用静态绑定,而您的C示例正好显示:调用1
的结果为f1
,因为这是f1
内打印的值。另一方面,图像不正确。您应该将环境视为一组帧,每个帧都包含连接到其他帧的绑定(即耦合变量,当前值)。因此f2
,f3
,E1
的关闭是不同的。
下图显示了全球环境的增长:
a1
是E2
定义后的全球环境。在f1
定义之后的f1
,您可以注意到f1
的值是指向第二帧的闭包(这允许递归定义,因为a1
可以在原则称自己)。在闭包中,E1
的值是E3
中的值。 f2
是定义a1
之后的环境,其中闭包首先指向本地环境,其中2
等于E4
,然后指向当前的全局环境。最后f3
是a2
定义后的全球环境。请注意,新闭包还有一个2
等于f3
的本地环境。
调用f1
时,f1
内的E2
将被检索为f1
中a1 = 1
的值,并且在评估f3
时,使用(define a1 2)
。
另一方面,使用动态绑定,不需要创建闭包。在当前环境中评估每个函数,该环境是使用参数绑定和本地定义扩展的全局环境。所以你可以想象全球环境现在只有这种形式:
但在评估(f1)
时,f1
会向环境添加新框架:
并使用此环境评估最后一个表单a2
。在此评估期间,在环境中检索2
,并在相同(唯一)环境中再次评估 正文,其中%c
值printf
。
答案 1 :(得分:1)
这里棘手的部分是f2
中的内部定义。
如果您尝试使用set!
代替define
的此计划
你会得到你期望的结果(为了清楚起见,我改名为最后一个
功能到f3
)。
(define a1 1)
(define (f1) a1)
(f1) ; return 1
(define (f2) (set! a1 2) a1)
(f2) ; return 2
(define (f3) (define a1 2) (f1))
(f3) ; return 1,not 2
输出:
1
2
2
现在您没有看到原始程序中的行为是由于的原因 内部定义:
(define (f2) (define a1 2) a1)
内部定义将扩展为
(define (f2)
(letrec ((a1 2))
a1))
如果我们重命名变量是:
(define (f2)
(letrec ((a2 2))
a2))
因此内部定义将分配一个新变量,因此a1绑定的原始位置不受影响 - 因此保持值为1。
注意:在REPL(顶级)中使用define
定义已定义的变量等同于`set!。这是顶级定义,内部定义是两个独立的概念。