SICP Section 3.1.1 - Local state in procedures see

2019-07-23 18:59发布

问题:

I am working my way through SICP. I am on Section 3.1.1 and looking at local state. I am evaluating these exercises in GNU Guile v2.0.11.

I did find a similar question about this section, but it seems not to address the point I am struggling with, or I am overly obtuse.

The two examples I am looking at are these:

(define new-withdraw
  (let ((balance 100))
    (lambda (amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))


(define (make-withdraw balance)
  (lambda (amount)
    (if (>= balance amount)
        (begin (set! balance (- balance amount))
               balance)
        "Insufficient funds")))

When I assign the first to a variable with:

(define a new-withdraw)
(define b new-withdraw)

I get two pointers to the same procedure object and the state is shared between them:

scheme@(guile-user)> a 
$1 = #<procedure 1165880 at /path/to/file (amount)>
scheme@(guile-user)> b
$2 = #<procedure 1165880 at /path/to/file (amount)>
scheme@(guile-user)> (a 50)
$3 = 50
scheme@(guile-user)> (b 10)
$4 = 40

When I implement the second procedure, though, I get pointers to two distinct procedure objects with distinct state:

scheme@(guile-user)> (define c (make-withdraw 100))
scheme@(guile-user)> (define d (make-withdraw 100))
scheme@(guile-user)> c
$5 = #<procedure 14fdac0 at /path/to/file (amount)>
scheme@(guile-user)> d
$6 = #<procedure 1508360 at /path/to/file (amount)>
scheme@(guile-user)> (c 50)
$7 = 50
scheme@(guile-user)> (d 10)
$8 = 90

I've read through the section and there is no clear explanation of this, and I'm having trouble finding anything when I search for this section. I understand what is happening in terms of state in general, but I don't get what the difference between these procedures is that allows one to have a single universal state, and the other to maintain local state.

Why is the first definition, 'new-withdraw', unable to maintain local state across multiple assignments? It seems that the lambda should be capturing the assignment of balance to 100 every time we make a different assignment (define new-withdraw).

回答1:

The reason is that in the second example you return a fresh closure every time, whereas in the first example there is only one closure, so only one state.

If you want the same behaviour in the first case as you have in the second case, change to:

(define (new-withdraw) ; this is a procedure now
  (let ((balance 100))
    (lambda (amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))))

(define a (new-withdraw)) ; called as a procedure
(define b (new-withdraw))

then

> (a 50)
50
> (b 10)
90