Consider the max-of-list function from the exam.  One solution was

;; max-of-list : non-empty-list-of-numbers -> num
;; return the largest number in the input list
(define (max-of-list anelon)
  (cond [(empty? (rest anelon)) (first anelon)]
	[(cons? (rest anelon))
	 (cond [(> (first anelon) (max-of-list (rest anelon)))
		(first anelon)]
	       [else (max-of-list (rest anelon))])]))

There's something unsatisfying about this program: in the cons? case,
we compute (max-of-list (rest anelon)) twice.  Writing the same
expression twice is unsatifying for several reasons:

- If we write an expression only once, then if we have to change it,
we only need to change it in one place.  This is called having a
single point of control.

- If the expression is long and tedious, we would rather not read (or
write) it more than once.

- It induces a lot of redundant computation.  How much?  Consider what
happens if we have a list containing 6 numbers.  Then we will compute
max-of-list twice on a list containing 5 numbers.  Each of these
computations will compute max-of-list twice on a list containing 4
numbers, and so on.  For a list containing n elements, we end up
calling max-of-list 2^(n-1) times.  For a large enough list (around 15
elements), you will notice DrScheme slowing down due to the extra
computation. 

Therefore, we want to be able to call max-of-list only once.  Today,
we introduce another piece of Scheme syntax (your first in a month) to
help with this situation: it's called local.  Here's how max-of-list
would appear using local.  (To use this, you'll need to start using
the Intermediate language level in DrScheme.)


;; max-of-list : non-empty-list-of-numbers -> num
;; return the largest number in the input list
(define (max-of-list anelon)
  (cond [(empty? (rest anelon)) (first anelon)]
	[(cons? (rest anelon))
	 (local ((define maxrest (max-of-list (rest anelon))))
     	   (cond [(> (first anelon) maxrest) (first anelon)]
		 [else maxrest]))]))

Why are there two parentheses between local and define?  Local takes a
list of definitions, each in proper Scheme definition format.  The
outer pair of parentheses enclose the list of definitions, which in
the above case has only one element.

Here's an example that uses two definitions:

(define (expt5 x)
  (local ((define (square y) (* y y))
	  (define (cube z) (* z (square z))))
    (* (square x) (cube x))))

What happens if we put this definition into the Definitions window and
evaluate (expt5 2) in the interactions window?  We get 32, as expected.
What if we evaluate (cube 3) in the interactions window?  We get an
error.  Why?

The definitions inside of a local are only visible within the
parentheses that enclose the local.  Since the multiplication is
inside the local, it can use the functions square and cube.  Once we
pass the parenthesis that closes (local, we can no longer use those
definitions. 

This raises an important issue in programming languages known as
scoping.  Scoping tells us where a definition is visible.  You've been
dealing with scoping all along, but perhaps without realizing it.
Consider what happens if you type x at the DrScheme prompt.  Unless
you've done a (define x ...), DrScheme complains that x is an
undefined identifier.  However, if you write (define (f x) (+ x 3)),
DrScheme doesn't complain because the x in (+ x 3) is within a
function that has x as a parameter.

Whenever you write a definition (define or define-struct), you are
telling DrScheme where it can use your definition.  Consider the
following example

(define (double x)
  (+ x x))

(define (some-math x)
  (local ((define (square y) (* y y))
	  (define (cube y) (* y (square y))))
    (+ (square x) (cube x)
       (local ((define z (- (cube x) 3)))
         (square (double z))))))

What this program computes is irrelevant.  What is relevant is where
each variable and definition is visible.  Double is visible
everywhere, because it is not inside of a local.  The programs square
and cube are only visible inside of the first local.  The identifier z
is only visible inside the second local.  The text contains several
examples of drawing boxes around the parts of a program where a
definition is visible.  Look at those and make sure you see how to
draw similar boxes on this example.

Finally, we need to discuss how DrScheme evaluates a local, so that
you know what programs using local actually do.  Let's return to our
earlier example expt5:

(define (expt5 x)
  (local ((define (square y) (* y y))
	  (define (cube z) (* z (square z))))
    (* (square x) (cube x))))

Evaluate (+ (expt5 2) 3).  What does this become according to our
evaluation rules?

(+ (local ((define (square y) (* y y))
	   (define (cube z) (* z (square z))))
     (* (square 2) (cube 2)))
   3)

Now, DrScheme evaluates

(local ((define (square y) (* y y))
	(define (cube z) (* z (square z))))
  (* (square 2) (cube 2)))

At this point, DrScheme copies your local definitions as if they were
in the definitions window (you don't see them appear there, but it is
as if a little person inside the machine moved them there).  However,
in order to keep someone from using them outside of their scope,
DrScheme renames them (and changes the names in your program to
reflect the new names).  You don't see any of these changes in the
text of your program -- this all goes on behind the scenes when
DrScheme evaluates your program.

Now, DrScheme can finish evaluating your program normally, to yield
the answer 35.  To summarize the semantics of local:

   ...top-level definitions..
   (local (defs)
    body)

becomes:
   ...top-level definitions..
   ...defs...                       (renamed for uniqueness)
   body                             (with renaming)