这里是make-counter
程序并调用它
(define make-counter
(let ((glob 0))
(lambda ()
(let ((loc 0))
(lambda ()
(set! loc (+ loc 1))
(set! glob (+ glob 1))
(list loc glob))))))
> (define counter1 (make-counter))
counter1
> (define counter2 (make-counter))
counter2
> (counter1)
(1 1)
> (counter1)
(2 2)
> (counter2)
(1 3)
> (counter1)
(3 4)
我无法理解为什么glob
表现为类变量,而loc
表现为实例变量。
答案 0 :(得分:6)
在运行代码的每个部分时可能最容易考虑。你评价
(define make-counter (let ((0 glob)) ...))
只需一次,所以让只评估一次。这意味着只有一个绑定,其值由 let 正文中的所有内容共享。现在,让的内容是什么?这是一个lambda函数,它变为 make-counter 的值:
(lambda () ; this function is the value of make-counter
(let ((loc 0)) ; so this stuff gets execute *each time* that
(lambda () ; make-counter is called
... ;
))) ;
现在,每次调用 生成计数器时,您评估(let((loc 0))(lambda()...)),它创建一个新的绑定并返回一个可以访问它的lambda函数(以及来自外部的全局绑定。
因此,调用 make-counter 的每个结果都可以访问 glob 的单绑定,以及访问结果绑定 loc 。
答案 1 :(得分:2)
让我们检查一下这个程序:
(define make-counter
(let ((g 0))
(lambda ()
(let ((l 0))
(lambda ()
(set! l (+ l 1))
(set! g (+ g 1))
(list l g))))))
该程序说明了抽象(lambda
- 表达式)如何创建
一个包含对自由变量的引用的闭包。
明确地查看和检查自由变量会很有帮助,
所以让我们假装我们想用一种语言运行上面的程序
不支持lambda
。换句话说,让我们尝试重写
该程序使用更简单的结构。
首先是摆脱任务。我们分配一个盒子 (认为长度为1的向量)可以容纳一个值。 然后,赋值可以使用set-box更改框保存的值!
; Assignment conversion: Replace assignable variables with boxes.
; The variables l and g are both assigned to
(define make-counter
(let ((g (box 0)))
(lambda ()
(let ((l (box 0)))
(lambda ()
(set-box! l (+ (unbox l) 1))
(set-box! g (+ (unbox g) 1))
(list (unbox l) (unbox g)))))))
这个程序相当于原版(试试吧!)。
下一步是使用其自由变量注释每个lambda:
(define make-counter
(let ((g (box 0)))
(lambda () ; g is free in lambda1
(let ((l (box 0)))
(lambda () ; g and l are free lambda2
(set-box! l (+ (unbox l) 1))
(set-box! g (+ (unbox g) 1))
(list (unbox l) (unbox g)))))))
现在我们已准备好用显式闭包替换lambda
。
关闭持有
i)没有自由变量的函数
ii)创建闭包时自由变量的值
我们将使用向量来存储i)和ii)。
(define (make-closure code . free-variables)
(apply vector code free-variables))
我们可以得到没有这样的自由变量的函数:
(define (closure-code closure)
(vector-ref closure 0))
我们可以像这样自由变量:
(define (closure-ref closure i)
(vector-ref closure (+ i 1)))
要应用闭包,可以调用没有自由变量的函数(代码) 两个闭包(哪个代码需要找到的值) 自由变量)和实际参数。
(define (apply-closure closure . args)
(apply (closure-code closure) closure args))
以下是与lambda1
对应的代码(define (lambda1 cl) ; cl = (vector lambda1 g)
(let ((g (closure-ref cl 0))) ; g is the first free variable of lambda1
(let ((l (box 0)))
(make-closure lambda2 g l))))
由于lambda1是无参数的函数,因此唯一的输入是闭包。 它首先要做的是检索自由值g。
注意lambda1返回一个闭包:( make-closure lambda2 g l) 这里我们看到当lambda2的闭包形成g和l的值时 被保留下来。
现在lambda2:
(define (lambda2 cl) ; cl = (vector lambda2 g l)
(let ((g (closure-ref cl 0))
(l (closure-ref cl 1)))
(set-box! l (+ (unbox l) 1))
(set-box! g (+ (unbox g) 1))
(list (unbox l) (unbox g))))
最后make-counter简单地创建了一个lambda1-closure:
(define make-counter (make-closure lambda1 (box 0)))
我们现在准备好了解我们的计划:
(define counter1 (apply-closure make-counter))
counter1
(define counter2 (apply-closure make-counter))
counter2
(apply-closure counter1)
(apply-closure counter1)
(apply-closure counter2)
(apply-closure counter1)
输出结果为:
'#(#<procedure:lambda2> #&0 #&0)
'#(#<procedure:lambda2> #&0 #&0)
'(1 1)
'(2 2)
'(1 3)
'(3 4)
这意味着程序的工作方式与原始程序相同。 但是现在我们可以检查两个计数器的自由变量:
> counter1
'#(#<procedure:lambda2> #&4 #&3)
> counter2
'#(#<procedure:lambda2> #&4 #&1)
我们可以检查两个计数器是否共享相同的g:
> (eq? (closure-ref counter1 0)
(closure-ref counter2 0))
#t
我们还可以检查他们有两个不同的包含l。
的框> (eq? (closure-ref counter1 1)
(closure-ref counter2 1))
#f