Lisp 用局部状态理解对象——Scheme

Lisp 用局部状态理解对象——Scheme,lisp,scheme,mit-scheme,Lisp,Scheme,Mit Scheme,我正在为我的计划期末考试而学习,与当地政府的合作一直是一个棘手的问题 这是我期末考试的一个问题,我需要帮助 (define (make-incrementer n) (let ((old 0) (new 0)) (lambda () (cond ((< new n) (set! old new) (set! new (+ new 1)) old)

我正在为我的计划期末考试而学习,与当地政府的合作一直是一个棘手的问题

这是我期末考试的一个问题,我需要帮助

(define (make-incrementer n)
  (let ((old 0)
        (new 0))
    (lambda ()
      (cond ((< new n) 
             (set! old new)
             (set! new (+ new 1))
             old)
            (else 
             (set! old 0)
             (set! new 1)
             old)))))

(define a (make-incrementer 3))
(define b (make-incrementer 3))
(define c a)

; 1) (a)
; 2) (a)
(定义(使增量为n)
(let((旧0)
(新的(0))
(lambda()
(cond(

为什么
a
在第二次调用时返回1?我在看代码,我们给出的
n
总是3。那么,它不总是做
其他的
案例吗?

欢迎来到闭包的奇妙世界!这是Scheme中闭包工作原理的教科书示例

因此,
make counter
返回一个函数,该函数包含从其封闭环境捕获的3个变量:
n
old
new
。在本例中,启动环境如下所示

_name_|_value_
 n    | 3
 old  | 0
 new  | 1
每次调用时,它都会递增
old
new
,如果它们大于
n
,则将它们环绕。因为它正在使用
set,此递增操作会使lambda环境中的变量发生变化,但由于这些变量是从周围环境捕获的,因此在将来的所有调用中也会更改这些变量

这就是为什么即使输入相同,也会得到不同的回报

如果这看起来像巫术,你可以把它想象成更普通语言中的物体:

例如Python:

class Foo():
    def __init__(self, n, m):
        self.n = n
        self.m = m
    def count(self):
        self.n += 1
        if self.n == self.m:
           self.n = 1
        return self.n-1

f = Foo(0, 2)

f.count() # 1
f.count() # 0
这是相同的基本思想,除了这里我们更明确地说明了环境的来源,
self
。在这个方案中,我们用lambda捕捉周围的变量来模拟这个过程


有关更多信息,请查看以下示例,这些示例可能有助于理解捕获状态的概念:

(define (always x) 
  (lambda rest x))
(define always-true (always #t))
(always-true #f)
-> #t

(define (add-n n) 
  (lambda (m)
    (+ n m)))
(define add-1 (add-n 1))
(add-1 10)
-> 11

(define (complement predicate)
  (lambda (x)
    (not (predicate x)))
(define not-positive? (complement positive?))
(not-positive? -1)
-> #t
接下来是一个示例,其中捕获的状态(在本例中为
l
)发生突变。这与捕获和修改
new
old
的情况类似

(define (nexting l)
  (lambda ()
    (if (null? l)
        '()
        (let ((answer (car l)))
          (set! l (cdr l))
          answer))))
(define next-user (nexting '(Alice Bob David)))
(next-user)
-> Alice
(next-user)
-> Bob
(next-user)
-> David
(next-user)
'()

有时,代码布局有助于理解,因为这使不同名称的范围显而易见。@GoZoner的确,感谢上帝在发布到SOim时使用了emacs中的C-M-\in和'M-x untabify',不幸的是,我没有emacs。@user2036503:GNU emacs可用于windows,我最后一次看了一下。我不明白的主要问题是,旧的和新的不一定要大于3才能使递增集起作用吗?@user2036503是的,但它们正在递增,
(set!new(+1 new))
我可能遗漏了一些非常明显的东西。对不起。但是,要使增量起作用,不必大于n。但是n等于3,初始的新值等于1,所以它从不递增。不用担心,它开始有意义了吗?也许我输入(a)和(a)的方式有点问题。但它不应该返回0,然后返回0。还是我错了?