Next: Dynamic Binding, Previous: Lambda Expressions, Up: Special Forms [Contents][Index]
The binding constructs let
, let*
, letrec
,
letrec*
, let-values
, and let*-values
give Scheme
block structure, like Algol 60. The syntax of the first four
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 bindings and evaluations are performed
sequentially; while in letrec
and letrec*
expressions,
all the bindings are in effect while their initial values are being
computed, thus allowing mutually recursive definitions. The
let-values
and let*-values
constructs are analogous to
let
and let*
respectively, but are designed to handle
multiple-valued expressions, binding different identifiers to the
returned values.
The inits are evaluated in the current environment (in some unspecified order), the variables are bound to fresh locations holding the results, the exprs are evaluated sequentially in the extended environment, and the value of the last expr is returned. Each binding of a variable has the exprs 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) …) expr expr …) ((lambda (variable …) expr expr …) 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)) expr expr …)
(let ((variable1 init1)) (let ((variable2 init2)) … (let ((variableN initN)) expr expr …) …))
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 exprs are evaluated
sequentially in the extended environment, and the value of the last
expr 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.
The variables are bound to fresh locations, each variable
is assigned in left-to-right order to the result of evaluating the
corresponding init (interleaving evaluations and assignments),
the exprs are evaluated in the resulting environment, and
the values of the last expr are returned. Despite the
left-to-right evaluation and assignment order, each binding of a
variable has the entire letrec*
expression as its region,
making it possible to define mutually recursive procedures.
If it is not possible to evaluate each init without assigning or referring to the value of the corresponding variable or the variable of any of the bindings that follow it in bindings, it is an error. Another restriction is that it is an error to invoke the continuation of an init more than once.
;; Returns the arithmetic, geometric, and ;; harmonic means of a nested list of numbers (define (means ton) (letrec* ((mean (lambda (f g) (f (/ (sum g ton) n)))) (sum (lambda (g ton) (if (null? ton) (+) (if (number? ton) (g ton) (+ (sum g (car ton)) (sum g (cdr ton))))))) (n (sum (lambda (x) 1) ton))) (values (mean values values) (mean exp log) (mean / /))))
Evaluating (means '(3 (1 4)))
returns three values: 8/3
,
2.28942848510666
(approximately), and 36/19
.
The inits are evaluated in the current environment (in some
unspecified order) as if by invoking call-with-values
, and the
variables occurring in the formals are bound to fresh locations
holding the values returned by the inits, where the
formals are matched to the return values in the same way that
the formals in a lambda
expression are matched to the
arguments in a procedure call. Then, the exprs are
evaluated in the extended environment, and the values of the last
expr are returned. Each binding of a variable has
the exprs as its region.
It is an error if the formals do not match the number of values returned by the corresponding init.
(let-values (((root rem) (exact-integer-sqrt 32))) (* root rem)) ⇒ 35
The let*-values
construct is similar to let-values
, but the
inits are evaluated and bindings created sequentially from
left to right, with the region of the bindings of each formals
including the inits to its right as well as body. Thus the
second init is evaluated in an environment in which the first
set of bindings is visible and initialized, and so on.
(let ((a 'a) (b 'b) (x 'x) (y 'y)) (let*-values (((a b) (values x y)) ((x y) (values a b))) (list a b x y))) ⇒ (x y x y)
Next: Dynamic Binding, Previous: Lambda Expressions, Up: Special Forms [Contents][Index]