Prev Up Next
Scheme's variables have lexical scope, ie, they are
visible only to forms within a certain contiguous
stretch of program text. The global variables we
have seen thus far are no exception: Their scope is all
program text, which is certainly contiguous.
We have also seen some examples of local
variables. These were the lambda parameters, which
get bound each time the procedure is called, and
whose scope is that procedure's body. Eg,
(define x 9)
(define add2 (lambda (x) (+ x 2)))
x => 9
(add2 3) => 5
(add2 x) => 11
x => 9
Here, there is a global x, and there is also a
local x, the latter introduced by procedure
add2. The global x is always
9. The local x gets bound to 3 in the
first call to add2 and to the value of the global
x, ie, 9, in the second call to add2.
When the procedure calls return, the global x
continues to be 9.
The form set! modifies the lexical binding of a
variable.
(set! x 20)
modifies the global binding of x from 9 to
20, because that is the binding of x that is
visible to set!. If the set! was inside
add2's body, it would have modified the local
x:
(define add2
(lambda (x)
(set! x (+ x 2))
x))
The set! here adds 2 to the local variable
x, and returns that value. (In terms of effect,
this procedure is indistinguishable from the previous
add2.) We can call add2 on the
global x, as before:
(add2 x) => 22
(Remember global x is now 20, not 9!)
The set! inside add2 affects only the local
variable used by add2. Although the local variable
x got its binding from the global x,
the latter is unaffected by the set! to the local
x.
x => 20
Note that we had all this discussion because we used
the same identifier for a local variable and a global
variable. In any text, an identifier named x refers
to the lexically closest variable named x. This
will shadow any outer or global x's. Eg,
in add2, the parameter x shadows the global
x.
A procedure's body can access and modify variables in
its surrounding scope provided the procedure's
parameters don't shadow them. This can give some
interesting programs. Eg,
(define counter 0)
(define bump-counter
(lambda ()
(set! counter (+ counter 1))
counter))
The procedure bump-counter is a zero-argument
procedure (also called a thunk). It introduces
no local variables, and thus cannot shadow anything.
Each time it is called, it modifies the global
variable
counter -- it increments it by 1 -- and returns
its current value. Here are some successive calls to
bump-counter:
(bump-counter) => 1
(bump-counter) => 2
(bump-counter) => 3
Prev Up Next