对SCHEME的一些理解(4)
SICP练习3.10
(define (make-withdraw initial-amount)
(let ((blance initial-amount))
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount)) balance)
"Insufficient funds"))))
我们先改写为标准lambda表达式:
(define make-withdraw
(lambda (initial-amount)
((lambda (balance)
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount)) balance)
"Insufficient funds")))
initial-amount)))
以上define语句执行后,会在全局环境E0中注册一个符号make-withdraw,其值为一个
过程对象,对象内容如下:
proc0:
--------------------------------
| env-pointer : E0 |
--------------------------------
| parameters : initial-amount |
--------------------------------
| code : |
| ((lambda (balance) |
| (lambda (amount) |
| ... |
--------------------------------
E0:
--------------------------------
| outter-env : null |
--------------------------------
| make-withdraw : proc0 |
--------------------------------
看看求值如下表达式会发生什么情况
(define W1 (make-withdraw 100))
define的作用是将一个符号和一个值联系起来,并存储于某个环境之中,所以以上语句
执行步骤如下:
1、对表达式(make-withdraw 100)求值,求值操作的环境为全局环境E0,组合表达式
求值应先求值子表达式,所以先求值符号make-withdraw,在E0环境中搜索到其值为一
过程对象proc0(如前所示),在求值“100”,100为自求值符号,返回值100。
2、将实参应用于操作符proc0。这一步将执行两个操作,一是创建新环境E1,
而proc0中的env-pointer : E0,所以E1的外部环境为E0,在E1环境中注
册符号:initial-amount : 100。
E1:
--------------------------------
| outter-env : E0 |
--------------------------------
| initial-amount : 100 |
--------------------------------
3、在新建环境E1中求值proc0的code:
((lambda (balance)
((lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount)) balance)
"Insufficient funds")))
initial-amount)
首先求值前面的的lambda表达式(lambda (balance)...,lambda表达式会在当前环境中
注册一个无名的函数对象proc-unnamed0(随意取的),无名函数必须为立即使用或传递
否则之后将无法调用。再求值符号initial-amount,在E1中搜索得到值为100。
proc-unnamed0:
--------------------------------
| env-pointer : E1 |
--------------------------------
| parameters : balance |
--------------------------------
| code : |
| ((lambda (amount) |
| (begin (set!.... |
| ... |
--------------------------------
4、将实参100应用到proc-unnamed0,与步骤2类似,创建环境E2,因为proc-unnamed0的
外部环境为E1,所以E2的外部环境设置为E1,在E2环境中注册变量balance : 100。
E2:
--------------------------------
| outter-env : E1 |
--------------------------------
| balance : 100 |
--------------------------------
5、在环境E2中求值proc-unnamed0的code,code为一lambda表达式,直接返回函数对象proc1与
W1符号链接,且注册到E0环境中。
proc1:
--------------------------------
| env-pointer : E2 |
--------------------------------
| parameters : amount |
--------------------------------
| code : |
| ((begin (set! balance... |
| ... |
--------------------------------
最后E0如下:
E0:
--------------------------------
| outter-env : null |
--------------------------------
| make-withdraw : proc0 |
--------------------------------
| W1 : proc1 |
--------------------------------
proc-unnamed0对象执行完毕后,因为没有被任何符号约束,所以可以直接释放掉。
所以目前的内存状态为:
E0 <- E1 <- E2
proc0 -> E0
proc1 -> E2
再来看执行(W1 50)会发生什么?
1、(W1 50)在全局环境E0中执行,首先求符号W1的值,在E0中搜索得到W1的约束,
返回值proc1,求值“50”,返回数值50。
2、将实参50应用到proc1。先创建环境E3,将约束amount : 50注册到E3中,关键时刻到了,
由于proc1的环境指针为E2,所以E3的外部环境为E2。
E3:
--------------------------------
| outter-env : E2 |
--------------------------------
| amount : 50 |
--------------------------------
3、在E3中求值proc1,既运行proc1的code,为if表达式,其中的amount在E3中约束,而balance
在E2中约束。
(define W2 (make-withdraw 100))与上同理,可见这种方法相比文中例子,多浪费了一个E1环境,
其中约束了一个无用符号initial-amount。