The resulting procedure is just as much a procedure
as one that is created using
define
. The only
difference is that it has not been associated with any name in the environment. In fact,
(define (plus4 x) (+ x 4))
is equivalent to
(define plus4 (lambda (x) (+ x 4)))
We can read a
lambda
expression as follows:
(lambda (x) (+ x 4))
the procedure of an argument x that adds x and 4
Like any expression that has a procedure as its value, a
lambda
expression can be used as the
operator
in a combination such as
((lambda (x y z) (+ x y (square z))) 1 2 3)
12
or, more generally, in any context where we would normally use a procedure name.
53
Using
let
to create local variables
Another use of
lambda
is in creating local variables. We often need local variables in our procedures
other than those that have been bound as formal parameters. For example, suppose we wish to
compute the function
which we could also express as
In writing a procedure to compute f, we would like to include as local variables not only x and y but
also the names of intermediate quantities like a and b. One way to accomplish this is to use an
auxiliary procedure to bind the local variables:
(define (f x y)
(define (f-helper a b)
(+ (* x (square a))
(* y b)
(* a b)))
(f-helper (+ 1 (* x y))
(- 1 y)))
Of course, we could use a
lambda
expression to specify an anonymous procedure for binding our
local variables. The body of
f
then becomes a single call to that procedure:
(define (f x y)
((lambda (a b)
(+ (* x (square a))
(* y b)
(* a b)))
(+ 1 (* x y))
(- 1 y)))
This construct is so useful that there is a special form called
let
to make its use more convenient.
Using
let
, the
f
procedure could be written as
(define (f x y)
(let ((a (+ 1 (* x y)))
(b (- 1 y)))
(+ (* x (square a))
(* y b)
(* a b))))
The general form of a
let
expression is
(let ((<var
1
> <exp
1
>)
(<var
2
> <exp
2
>)
(<
var
n
> <exp
n
>))
<
body>)
which can be thought of as saying
let
<var
1
> have the value <exp
1
> and
<
var
2
> have the value <exp
2
> and
<
var
n
> have the value <exp
n
>
in
<
body>
The first part of the
let
expression is a list of name-expression pairs. When the
let
is evaluated,
each name is associated with the value of the corresponding expression. The body of the
let
is
evaluated with these names bound as local variables. The way this happens is that the
let
expression
is interpreted as an alternate syntax for
((lambda (<var
1
> ...<var
n
>)
<body>)
<
exp
1
>
<
exp
n
>)
No new mechanism is required in the interpreter in order to provide local variables. A
let
expression
is simply syntactic sugar for the underlying
lambda
application.
We can see from this equivalence that the scope of a variable specified by a
let
expression is the
body of the
let
. This implies that:
Let
allows one to bind variables as locally as possible to where they are to be used. For example,
if the value of
x
is 5, the value of the expression
(+ (let ((x 3))
(+ x (* x 10)))
x)
is 38. Here, the
x
in the body of the
let
is 3, so the value of the
let
expression is 33. On the
other hand, the
x
that is the second argument to the outermost
+
is still 5.
The variables’ values are computed outside the
let
. This matters when the expressions that
provide the values for the local variables depend upon variables having the same names as the
local variables themselves. For example, if the value of
x
is 2, the expression
(let ((x 3)
(y (+ x 2)))
(* x y))
will have the value 12 because, inside the body of the
let
,
x
will be 3 and
y
will be 4 (which is
the outer
x
plus 2).
Sometimes we can use internal definitions to get the same effect as with
let
. For example, we could
have defined the procedure
f
above as
(define (f x y)
(define a (+ 1 (* x y)))
(define b (- 1 y))
(+ (* x (square a))
(* y b)
(* a b)))
We prefer, however, to use
let
in situations like
this and to use internal
define
only for internal
procedures.
54
Exercise 1.34. Suppose we define the procedure
(define (f g)
(g 2))
Then we have
(f square)
4
(f (lambda (z) (* z (+ z 1))))
6