Scheme中set!修改let变量及两种计数器实现差异解析
Scheme 计数器实现的差异解惑
背景
在自学MIT 6.5151课程过程中,我完成了这些阅读任务:
- 按ps0要求读完SICP第1至2.1章
- 参照CS 61A笔记阅读了SICP 2.2.1章
- 读完《Software Design for Flexibility》(简称SDF)的序言、第1章,以及部分Scheme附录
问题描述
在SDF的Scheme附录里看到一个用set!实现计数器的示例:
(define (make-counter) (let ((count 0)) (lambda () (set! count (+ count 1)) count)))
定义c1、c2之后,两个计数器拥有各自独立的局部状态,调用(c1)会持续返回递增的数值。
我自己写了一段类似的代码:
(define (count) (let ((cont 0)) (lambda () (set! cont (+ cont 1)) cont)))
但在MIT-Scheme里每次执行((count)),结果都是1。当时我疑惑,是不是因为每次调用((count))都会生成一个新的lambda实例?
问题解析(结合SICP第3.2章内容)
后来读完SICP第3.2章,我终于搞懂了:
- 原示例中,
(make-counter)被调用一次后,会返回一个绑定了特定环境的lambda(比如(define c1 (make-counter))),这个环境里的count初始值是0。之后每次调用(c1),都是在同一个环境里修改count的值,所以能实现持续递增。 - 而我写的
count过程,每次调用(count)都会构造一个全新的环境,其中的cont被重新初始化为0,然后返回绑定这个新环境的lambda。当执行((count))时,相当于先调用count生成新环境和lambda,再执行这个lambda——每次都是在全新的环境里把0加1,结果自然一直是1。这个逻辑和SICP里的(make-withdraw 100)是类似的,每次调用构造新环境,状态无法持续保留。
内容的提问来源于stack exchange,提问作者An5Drama
相关产品推荐
相关产品推荐

