Environment Diagrams
Long live the Environment Model. This is how things Really Work, so it's important to know. The best reference for how to do things in the environment model is in your Volume II reader on page 320 (or page 54 of the PDF lecture notes), and the most important lines (marked with asterisks) are repeated here:
- New frames are only created by (non-primitive) procedure calls.
- New procedures are only created by
lambda
.
There are two more important rules (they're in the reader but not emphasized on that page):
- When calling a procedure in Scheme, you extend the environment where the procedure was defined (its right-bubble pointer). This is because of Scheme's lexical scope. (Later on, we'll see there are other options!)
- Mutation only changes one arrow ("pointer") in the environment diagram. This applies to
set!
, but also to next week'sset-car!
andset-cdr!
. You have to remember to work with pointers and pairs when dealing with mutation, rather than entire lists.
A lot of people are often confused between the difference between a frame and an environment. Independent of the diagrams, a frame is a map of variable names and the values they are bound to. An environment is a list of frames, which is traversed in order when you look up the value of a variable.
In the diagrams, one rectangle is a frame. The chain of rectangles and arrows back to the global environment is an environment. (In Scheme, every environment is an extension of the global environment.)
We often use the term "global environment" when we really mean "global frame". Since the global environment is the list of one frame, though (the global frame), we are at least not being ambiguous about, say, where a variable has been defined.
EnvDraw and Practice Problems
What's EnvDraw, you ask? It draws environment diagrams (and box-and-pointer diagrams) for you! To use it, run the program envdraw at the shell (instead of emacs& or stk). This starts a special version of STk running an interpreter that draws any changes to the environment in a window. You can also use M-x run-envdraw in Emacs to run it in the current split.
Note that you can't use define-class
with EnvDraw, so you can't make it automatically show you the below-the-line representation of a class.
If you're connecting from home, make sure you turn on X forwarding (running Xming or Xceed on Windows, or using ssh -X on Mac or Linux), just like you did for the picture project.
For these practice problems, draw the environment, then log into an inst machine and use EnvDraw to check your answers. Don't skip steps, just go very carefully!
-
> (define (square x) (* x x)) > (square 5)
There should be 1 procedure and 1 frame in your solution, not counting the global frame.
-
> (define (square x) (define (times x y) (* x y)) (times x x) ) > (square 5)
There should be 2 procedures and 2 frames in your solution, not counting the global frame.
-
> (define (square x) (define (times y) (* x y)) (times x) ) > (square 5)
There should be 2 procedures and 2 frames in your solution, not counting the global frame.
-
> (define (square x) (define (times y) (* x y)) (times x) ) > (square 5) > (square 6)
There should be 3 procedures and 4 frames in your solution, not counting the global frame.
-
> (define balance (let ((init-balance 100)) balance) ) > balance
There should be 1 procedure and 1 frame in your solution, not counting the global frame. Nothing should point to the procedure.
-
> (define (withdraw amt) (let ((balance 100)) (- balance amt) )) > (withdraw 5) > (withdraw 10)
There should be 3 procedures and 4 frames in your solution, not counting the global frame.
-
> (define withdraw (let ((balance 100)) (lambda (amt) (- balance amt)) )) > (withdraw 5) > (withdraw 10)
There should be 2 procedures and 3 frames in your solution, not counting the global frame.
-
The last example creates the right environment structure, but still doesn't remember withdrawals! What do we need to do to fix this? Would it fix the previous version of
withdraw
?In order to change the value of a variable, we need to use
set!
.(lambda (amt) (set! balance (- balance amt)) balance)
Would this fix the first
withdraw
? No; thelet
is re-evaluated each call, meaning the balance gets reset each time.
Back to Jordy's home page | Course home page