2.4. Variables and Let Expressions


2.4. Variables and Let Expressions

Suppose expr is a Scheme expression that contains a variable var. Suppose, additionally, that we would like var to have the value val when we evaluate expr. For example, we might like x to have the value 2 when we evaluate (+ x 3). Or, we might want y to have the value 3 when we evaluate (+ 2 y). The following examples demonstrate how to do this using Scheme's let syntactic form.

 (let ((x 2))   (+ x 3))  5 (let ((y 3))   (+ 2 y))  5 (let ((x 2) (y 3))   (+ x y))  5 

The let syntactic form includes a list of variable-expression pairs, along with a sequence of expressions referred to as the body of the let. The general form of a let expression is

 (let ((var val) ...) exp1 exp2 ...) 

We say the variables are bound to the values by the let. We refer to variables bound by let as let-bound variables.

A let expression is often used to simplify an expression that would contain two identical subexpressions. Doing so also ensures that the value of the common subexpression is computed only once.

 (+ (* 4 4) (* 4 4))  32 (let ((a (* 4 4)))   (+ a a))  32 (let ((list1 '(a b c)) (list2 '(d e f)))   (cons (cons (car list1)               (car list2))         (cons (car (cdr list1))               (car (cdr list2)))))  ((a . d) b . e) 

Since expressions in the first position of a procedure application are evaluated no differently from other expressions, a let-bound variable may be used there as well.

 (let ((f +))   (f 2 3))  5 (let ((f +) (x 2))   (f x 3))  5 (let ((f +) (x 2) (y 3))   (f x y))  5 

The variables bound by let are visible only within the body of the let.

 (let ((+ *))   (+ 2 3))  6 (+ 2 3)  5 

This is fortunate, because we would not want the value of + to be the multiplication procedure everywhere.

It is possible to nest let expressions.

 (let ((a 4) (b -3))   (let ((a-squared (* a a))         (b-squared (* b b)))     (+ a-squared b-squared)))  25 

When nested let expressions bind the same variable, only the binding created by the inner let is visible within its body.

 (let ((x 1))   (let ((x (+ x 1)))     (+ x x)))  4 

The outer let expression binds x to 1 within its body, which is the second let expression. The inner let expression binds x to (+ x 1) within its body, which is the expression (+ x x). What is the value of (+ x 1)? Since (+ x 1) appears within the body of the outer let but not within the body of the inner let, the value of x must be 1 and hence the value of (+ x 1) is 2. What about (+ x x)? It appears within the body of both let expressions. Only the inner binding for x is visible, so x is 2 and (+ x x) is 4.

The inner binding for x is said to shadow the outer binding. A let-bound variable is visible everywhere within the body of its let expression except where it is shadowed. The region where a variable binding is visible is called its scope. The scope of the first x in the example above is the body of the outer let expression minus the body of the inner let expression, where it is shadowed by the second x. This form of scoping is referred to as lexical scoping, since the scope of each binding can be determined by a straightforward textual analysis of the program.

Shadowing may be avoided by choosing different names for variables. The expression above could be rewritten so that the variable bound by the inner let is new-x.

 (let ((x 1))   (let ((new-x (+ x 1)))     (+ new-x new-x)))  4 

Although choosing different names can sometimes prevent confusion, shadowing can help prevent the accidental use of an "old" value. For example, with the original version of the preceding example, it would be impossible for us to mistakenly refer to the outer x within the body of the inner let.

Exercise 2.4.1.

start example

Rewrite the following expressions, using let to remove common subexpressions and to improve the structure of the code. Do not perform any algebraic simplifications.

 a. (+ (- (* 3 a) b) (+ (* 3 a) b)) b. (cons (car (list a b c)) (cdr (list a b c))) 
end example

Exercise 2.4.2.

start example

Determine the value of the following expression. Explain how you derived this value.

 (let ((x 9))   (* x      (let ((x (/ x 3)))        (+ x x)))) 
end example

Exercise 2.4.3.

start example

Rewrite the following expressions to give unique names to each different let-bound variable so that none of the variables is shadowed. Verify that the value of your expression is the same as that of the original expression.

 a. (let ((x 'a) (y 'b))      (list (let ((x 'c)) (cons x y))            (let ((y 'd)) (cons x y)))) b. (let ((x '((a b) c)))      (cons (let ((x (cdr x)))              (car x))            (let ((x (car x)))              (cons (let ((x (cdr x)))                      (car x))                    (cons (let ((x (car x)))                            x)                          (cdr x)))))) 
end example




The Scheme Programming Language
The Scheme Programming Language
ISBN: 026251298X
EAN: 2147483647
Year: 2003
Pages: 98

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net