make-counter, который сохраняет состояние переменной следующим - PullRequest
0 голосов
/ 28 марта 2020

Я прочитал такой make-counter пример из Раздел 2.9. Назначение программирования схемы

> (define make-counter
    (lambda ()
      (let ([next 0])
        (lambda ()
          (let ([v next])
            (set! next (+ next 1))
            v)))))
> (define count1 (make-counter))
> (count1)
0
> (count1)
1

Я очень запутался здесь, как поддерживается состояние «следующий»?

с моим пониманием,
(define count1 (make-counter)), вернуть значение v, затем процедура make-counter исчезла и уничтожена,

Когда count1 был вызван снова, a fre sh make-counter вызывается, поэтому ожидаемые результаты должны быть постоянно "0".

Тем не менее, он волшебным образом печатает следующее:

> (count1)
1
> (count1)
2
> (count1)
3
> (count1)
4
> (count1)
5

Как сохраняется состояние «next»?

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

Это концепция замыкания , представляющая собой пару указателя на окружение и указателя на код. Переменная из кода указывает на среду замыкания. Каждый раз, когда вы звоните производителю, код получает доступ к среде замыкания и обновляет его.

Вы можете сделать это проще, так:

(define make-counter
  (lambda(k)    ;; closure encloses the global env. containing `+`, `-`, `set!`, etc.
    (lambda()   ;; internal closure encloses an environment containing k
      (set! k (+ 1 k))  ;; each call of (counter) mutates `k`
      (- k 1))))
(define counter (make-counter 0))
(counter)
0
(counter)
1
(counter)
2
0 голосов
/ 28 марта 2020

При повторном вызове count1 вызывается fre sh make-counter, поэтому ожидаемые результаты должны постоянно быть "0".

Нет, make-counter - это не звонил снова. Снова вызывается самая внутренняя лямбда, потому что (make-counter) вернула самую внутреннюю лямбду.

Чтобы увидеть это более четко, перепишите make-counter следующим образом:

(define (make-counter)
  (let ([next 0])
    (lambda ()  ; <- This lambda is the return value of `(make-counter)`.
      (let ([v next])
        (set! next (+ next 1))
        v))))

Чтобы увидеть это еще яснее, избавьтесь от make-counter:

> (define count1 (let ([next 0])
                   (lambda ()
                     (let ([v next])
                       (set! next (+ next 1))
                       v))))
> (count1)
0
> (count1)
1

Каждый раз, когда вы выполняете (count1), вы выполняете тело лямбды.

Даже если вы вызываете (count1) снова и снова, next не станет 0, потому что (count1) будет выполнять только тело лямбды. next был установлен на 0 только тогда, когда count1 был первоначально определен.

...