关于SICP中make-account过程的词法作用域与对象共享问询
Great question! Let's dive into how lexical scoping and object sharing work in this classic SICP make-account example—this is fundamental to understanding state and closures in functional programming.
Lexical Scoping in make-account
First, let's unpack lexical scoping here. When you call (make-account 100), it creates a new environment where:
- The variable
balanceis initialized to 100. - The inner procedures
withdraw,deposit, anddispatchare defined within this environment.
Lexical scoping means these inner procedures don't just know about their own parameters—they retain access to the variables in the environment where they were defined (in this case, the balance variable from make-account's call). This is what makes closures work in Scheme.
For example, when you run:
(define peter-acc (make-account 100)) ((peter-acc 'withdraw) 50) ; Returns 50
The withdraw procedure returned by (peter-acc 'withdraw) is still "connected" to the original balance variable created when make-account was called for Peter's account. Even though balance isn't a parameter to withdraw, lexical scoping lets it reference that outer variable—and when it uses set! to modify balance, it's changing that original variable in the closed-over environment.
Object Sharing with paul-acc
Now let's look at the object sharing part in (define paul-acc peter-acc).
When you define paul-acc as equal to peter-acc, you're not creating a new account. Instead, you're making paul-acc reference the same dispatch procedure that peter-acc points to. Since that dispatch procedure is part of the same closed environment with the original balance variable, both peter-acc and paul-acc are operating on the exact same state.
Let's test this with code:
(define peter-acc (make-account 100)) (define paul-acc peter-acc) ((peter-acc 'withdraw) 30) ; Balance becomes 70, returns 70 ((paul-acc 'deposit) 20) ; Balance becomes 90, returns 90 ((peter-acc 'withdraw) 10) ; Balance becomes 80, returns 80
Every operation through either peter-acc or paul-acc modifies the same balance—they're sharing the account object.
To contrast, if you had written (define paul-acc (make-account 100)), you'd be creating a new environment with its own balance variable. In that case, Peter and Paul's accounts would be completely independent, because each make-account call creates a fresh closed environment thanks to lexical scoping.
Key Takeaway
- Lexical scoping lets inner procedures (like
withdraw/deposit) retain access to thebalancefrom their defining environment, enabling stateful behavior in a functional language. - Sharing objects (like assigning
paul-acc = peter-acc) means multiple references point to the same stateful object—changes through one affect all others.
内容的提问来源于stack exchange,提问作者lightning_missile

