3.2.3 帧作为当前状态的仓库
我们能返回环境模型,来看看程序和赋值如何能被用来表示
有局部状态的对象。作为一个例子,考虑3.1.1部分中创建的
取款例子。由如下的程序:

(define (make-withdraw balance)
    (lambda (amount)
       (if (>= balance amount)
           (begin (set! balance (- balance amount)) balance)
    "Insufficient funds"
       )
    )
)

让我们描述如下的表达式的解释
(define W1 (make-withdraw 100))
如下:
(W1 50)
50

图3.6显示了在全局环境中定义程序make-withdraw的结果。
这产生了一个程序对象,它包括了一个指向全局环境的指针。
所以,这从我们已经看到的例子中没有什么不同,除了程序的
程序体本身是一个lambda表达式。

3.2.3 帧作为当前状态的仓库

 
图3.6 在全局环境中定义程序make-withdraw的结果

当我们应用make-withdraw到一个实际参数时,计算的有趣的部分
发生了。

(define W1 (make-withdraw 100))

我们像平常一样的开始,安装一个环境E1,并且形式参数被绑定到100。
在这个环境中,我们解释make-withdraw的程序体,是命名的Lambda表达式。
这组装了一个新的程序对象,它的代码被Lambda表达式指定,它的环境是'环境1"
在环境中Lambda表达式被解释,来生成程序.通过调用make-withdraw,
这个结果的程序对象是返回的值。在全局变量中,这被绑定到W1,在全局变量中,
define被解释的原因。图3.7显示了结果的环境结构。

3.2.3 帧作为当前状态的仓库

 
图3.7 解释(define W1 (make-withdraw 100))的结果

现在我们能分析当W1被应用到一个实际参数时,发生了什么:
(W1 50)
50

我们开始组装一个帧,它有一个amount,是W1的形式参数,被绑定到实际参数50。
注意的重要的点是这个帧的父环境不是全局环境,而是环境1。因为这是被W1
这个程序对象指定的环境。有了这个新的环境,我们解释程序的程序体:

(if (>= balance amount)
    (begin (set! balance (- balance amount))
        balance
    )
    "Insufficient funds"
)

在图3.8中显示的结果的环境结构。被解释的表达式引用了amount 和balance。
Amount在环境的第一个帧中发现了,balance在如下的父环境指向环境1中,被发现。

3.2.3 帧作为当前状态的仓库

 
图3.8 应用程序对象W1创建的环境

当set!被执行时,在环境1中的balance的绑定被改变了。W1
调用的完成,balance是50,包括了balance的帧仍然指向程序对象W1。
绑定了amount的帧不再相关,因为组装它的程序调用已经中止了,
并且没有指针从环境的其它部分指向这个帧了。W1的下一次调用,
这仍然构建了一个绑定了amount的新帧,它的父环境是环境1。我们能看到
环境1作为存储程序对象W1的局部变量的一个地方。图3.9显示了调用了
W1之后的情况。

3.2.3 帧作为当前状态的仓库

 

图3.9 调用了W1之后的环境

通过对make-withdraw做第二次调用,当我们创建了第二个“withdraw”对象时,
注意一下发生了什么:

(define W2 (make-withdraw 100))

这产生了图3.10中的环境结构,它显示出W2是一个程序对象,也就是一个数对,
这个数对包括了一些代码和一个环境。为W2创建的环境E2是通过调用make-withdraw
而创建的。它包括了一个帧,有它自己的对balance的局部绑定。另一个方面,W1和W2
有相同的代码,代码是make-withdraw的程序体中的Lambda表达式指定的。
我们这里看到W1和W2为什么行为上是独立的对象。对W1的调用引用的状态变量balance
存储在环境1中,对W2的调用引用的状态变量balance存储在环境2中。因此对一个对象的
局部的状态的修改 不影响到另一个对象。

3.2.3 帧作为当前状态的仓库

 
图3.10 使用(define W2 (make-withdraw 100))创建的第二个对象。

练习3.10
在make-withdraw程序中,局部变量balance被创建作为make-withdraw的一个参数.
我们也显式的创建了局部状态变量,使用Let,如下:

(define (make-withdraw inital-amount)
   (let ((balance inital-amount))
    (lambda (amount)
       (if (>= balance amount)
           (begin (set! balance (- balance amount)) balance)
    "Insufficient funds"
       )
    )
   )
)

重调用从1.3.2部分,Let是一个简单的为了程序调用的语法糖:

(let ( (<var1> <exp1>)
       (<var2> <exp2>)
 .....
       (<varn> <expn>)
      )
    <body>
)
能被解释为如下的替换的语法:

((lambda (<var1> ...<varn>)
     <body>)
   <exp1> ....<expn>)

使用环境模型来分析make-withdraw的替换版本,画出
如上的演示交互的图。

(define W1 (make-withdraw 100))

(W1 50)

(define W2 (make-withdraw 100))

显示创建有相同的行为的对象的make-withdraw的两个版本。
这两个版本的环境结构有什么不同呢?

相关文章:

  • 2022-02-12
  • 2021-09-20
  • 2022-12-23
  • 2022-12-23
  • 2021-05-18
  • 2022-12-23
猜你喜欢
  • 2021-11-10
  • 2022-12-23
  • 2022-12-23
  • 2021-07-06
  • 2021-11-10
  • 2022-02-08
相关资源
相似解决方案