Next: Dynamic Binding, Previous: Lambda Expressions, Up: Special Forms [Contents][Index]
The three binding constructs let
, let*
, and letrec
,
give Scheme block structure. The syntax of the three constructs is
identical, but they differ in the regions they establish for their
variable bindings. In a let
expression, the initial values are
computed before any of the variables become bound. In a let*
expression, the evaluations and bindings are sequentially interleaved.
And in a letrec
expression, all the bindings are in effect while
the initial values are being computed (thus allowing mutually recursive
definitions).
The inits are evaluated in the current environment (in some unspecified order), the variables are bound to fresh locations holding the results, the expressions are evaluated sequentially in the extended environment, and the value of the last expression is returned. Each binding of a variable has the expressions as its region.
MIT/GNU Scheme allows any of the inits to be omitted, in which case the corresponding variables are unassigned.
Note that the following are equivalent:
(let ((variable init) …) expression expression …) ((lambda (variable …) expression expression …) init …)
Some examples:
(let ((x 2) (y 3)) (* x y)) ⇒ 6
(let ((x 2) (y 3)) (let ((foo (lambda (z) (+ x y z))) (x 7)) (foo 4))) ⇒ 9
See Iteration, for information on “named let
”.
let*
is similar to let
, but the bindings are performed
sequentially from left to right, and the region of a binding is that
part of the let*
expression to the right of the binding. Thus
the second binding is done in an environment in which the first binding
is visible, and so on.
Note that the following are equivalent:
(let* ((variable1 init1) (variable2 init2) … (variableN initN)) expression expression …)
(let ((variable1 init1)) (let ((variable2 init2)) … (let ((variableN initN)) expression expression …) …))
An example:
(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) ⇒ 70
The variables are bound to fresh locations holding unassigned
values, the inits are evaluated in the extended environment (in
some unspecified order), each variable is assigned to the result
of the corresponding init, the expressions are evaluated
sequentially in the extended environment, and the value of the last
expression is returned. Each binding of a variable has the
entire letrec
expression as its region, making it possible to
define mutually recursive procedures.
MIT/GNU Scheme allows any of the inits to be omitted, in which case the corresponding variables are unassigned.
(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) #f (even? (- n 1)))))) (even? 88)) ⇒ #t
One restriction on letrec
is very important: it shall be possible
to evaluated each init without assigning or referring to the value
of any variable. If this restriction is violated, then it is an
error. The restriction is necessary because Scheme passes arguments by
value rather than by name. In the most common uses of letrec
,
all the inits are lambda
or delay
expressions and
the restriction is satisfied automatically.
Next: Dynamic Binding, Previous: Lambda Expressions, Up: Special Forms [Contents][Index]