# Naming Values with Local Bindings

This lab is also available in PDF.

Summary: In this laboratory, you will ground your understanding of the basic techniques for naming values and procedures in Scheme, `let` and `let*`.

Contents:

## Preparation

Create a new 20x20 image and name it `canvas`.

## Exercises

### Exercise 1: Evaluating `let`

What are the values of the following `let`-expressions? You may use DrScheme to help you answer these questions, but be sure you can explain how it arrived at its answers.

a.

```(let ((tone "fa")
(call-me "al"))
(list call-me tone "l" tone))
```

b.

```(let ((total (+ 8 3 4 2 7)))
(let ((mean (/ total 5)))
(* mean mean)))
```

c.

```(let ((inches-per-foot 12)
(feet-per-mile 5280.0))
(let ((inches-per-mile (* inches-per-foot feet-per-mile)))
(* inches-per-mile inches-per-mile)))
```

### Exercise 2: Nesting Lets

a. Write a nested `let`-expression that binds a total of five names, `alpha`, `beta`, `gamma`, `delta`, and `epsilon`, with `alpha` bound to a color of your choice (such as `color.red`), and each subsequent name bound to a value darker than it (computed by `rgb.darker`). That is, `beta` should be a darker version of `alpha`, `gamma` a darker version of `beta`, and so on and so forth. The body of the innermost let should list the five colors.

Your result will look something like

```(let ((...))
(let ((...))
(let ((...))
(let ((...))
(let ((...))
(list alpha beta gamma delta epsilon))))))
```

Remember that `(rgb.darker color)` gives a darker version of color.

b. Name the result of the computation using `define`.

```(define colors
(let ((...))
(let ((...))
(let ((...))
(let ((...))
(let ((...))
(list alpha beta gamma delta epsilon)))))))
```

c. Look at the values in that list with

```> (map rgb->string colors)
```

d. Put some colors in `canvas` with

```(region.compute-pixels!
canvas
0 0
(- (image.width canvas) 1) (- (image.height canvas) 1)
(lambda (pos) (list-ref colors
(modulo (* (position.col pos) (position.row pos))
5))))
```

### Exercise 3: Simplifying Nested Lets

a. Write a `let*`-expression equivalent to the `let`-expression in the previous exercise, but using a different starting color.

b. Repeat steps b and c of the previous problem.

### Exercise 4: Ordering Bindings

In the reading, we noted that it is possible to move bindings outside of the lambda in a procedure definition. In particular, we noted that the first of the two following versions of `years-to-seconds` required recomputation of `seconds-per-year` every time it was called while the second required that computation only once.

```(define years-to-seconds-a
(lambda (years)
(let* ((days-per-year 365.24)
(hours-per-day 24)
(minutes-per-hour 60)
(seconds-per-minute 60)
(seconds-per-year (* days-per-year hours-per-day
minutes-per-hour seconds-per-minute)))
(* years seconds-per-year))))

(define years-to-seconds-b
(let* ((days-per-year 365.24)
(hours-per-day 24)
(minutes-per-hour 60)
(seconds-per-minute 60)
(seconds-per-year (* days-per-year hours-per-day
minutes-per-hour seconds-per-minute)))
(lambda (years)
(* years seconds-per-year))))
```

Here's a procedure that makes it easy to tell when an expression is being used. It prints the value it is called with and then returns the value.

```(define value
(lambda (val)
(display "Computed: ")
(display val)
(newline)
val))
```

a. Using value, confirm that `years-to-seconds-a` does, in fact, recompute the values each time it is called. You might, for example, replace

```           (seconds-per-year (* days-per-year hours-per-day
minutes-per-hour seconds-per-minute)))
```

with

```           (seconds-per-year (value (* days-per-year hours-per-day
minutes-per-hour seconds-per-minute))))
```

b. Confirm that `years-to-seconds-b` does not recompute the values each time it is called. Again, make changes like those reported above.

c. Given that `years-to-seconds-b` does not recompute each time, when does it do the computation? (Consider when you see the messages.)

### Exercise 5: Ordering Bindings, Revisited

You may recall that we defined a procedure to compute a grey with the same brightness as a original color.

```(define rgb.greyscale
(lambda (color)
(let ((brightness (+ (* 0.30 (rgb.red color))
(* 0.59 (rgb.green color))
(* 0.11 (rgb.blue color)))))
(rgb.new brightness brightness brightness))))
```

You might be tempted to move the `let` clause outside the lambda, just as we did in the previous exercise. However, as we noted in this reading, this reordering will fail.

a. Verify that it will not work to move the `let` before the lambda, as in

```(define rgb.greyscale
(let ((brightness (+ (* 0.30 (rgb.red color))
(* 0.59 (rgb.green color))
(* 0.11 (rgb.blue color)))))
(lambda (color)
(rgb.new brightness brightness brightness))))
```

b. Explain, in your own words, why this fails (and why it should fail).

### Exercise 6: Faster Blends

Here's a procedure that one might use to make a blend between two colors.

```(define image.fill-with-blend!
(lambda (image start-color end-color)
(region.compute-pixels!
image
0 0
(- (image.width image) 1) (- (image.height image) 1)
(lambda (pos)
(rgb.new (+ (* (position.col pos)
(/ 1 (- (image.width image) 1))
(rgb.red end-color))
(* (- (image.width image) (position.col pos) 1)
(/ 1 (- (image.width image) 1))
(rgb.red start-color)))
(+ (* (position.col pos)
(/ 1 (- (image.width image) 1))
(rgb.green end-color))
(* (- (image.width image) (position.col pos) 1)
(/ 1 (- (image.width image) 1))
(rgb.green start-color)))
(+ (* (position.col pos)
(/ 1 (- (image.width image) 1))
(rgb.blue end-color))
(* (- (image.width image) (position.col pos) 1)
(/ 1 (- (image.width image) 1))
(rgb.blue start-color))))))))
```

a. Create a blend from magenta to yellow in a five-by-five image. Then, find the largest square image you can fill with a blend from magenta to yellow in under fifteen seconds. (You need not get an exact size, just a close approximation.)

b. Identify common expressions that are repeated in `image.fill-with-blend!`. When you are done, compare your list to those in the notes on this part of the problem.

c. Which of these common expressions have values that are independent of the position?

d. Write a `let` or `let*` expression to bind names to these expressions. Make sure that this `let` encloses the call to `region.compute-pixels!`.

When you are done, compare your answer to the notes on this part of the problem.

e. Run the modified code on a five-by-five image to see if it appears to be any faster. Then, find the largest square image you can fill with a blend from magenta to yellow in under fifteen seconds. (Again, you need not get an exact size, just a reasonable approximation.)

f. Which of the common expressions from step b have values that depend on the position?

g. Write a `let` or `let*` expression to bind names to those pieces of code. For example, since `(position.col pos)` appears three times, you might name it `col`.

When you are done, compare your answer to the notes on this part of the problem.

h. Run the modified code on the same image size as you came up with for step e see if it appears to be any faster.

i. There are a few expressions, such as `(rgb.red end-color)`, that appear only once, but that have an identical value each time the inner lambda is applied. Name those expressions in the outer `let`.

j. Compare your final version to the notes on this part of the problem.

k. What advantages do you see to this final version?

## For Those With Extra Time

### Extra 1: Finding the Leftmost Spot

Here is a procedure that takes a non-empty list of spots as an argument, and returns the leftmost spot in the list (or one of the leftmost spots, if there is a tie).

```;;; Procedure:
;;;   spots.leftmost
;;; Parameters:
;;;   spots, a list of spots
;;; Purpose:
;;;   Finds the leftmost of spots.
;;; Produces:
;;;   leftmost, a spot
;;; Preconditions:
;;;   spots is nonempty.
;;; Postconditions:
;;;   leftmost is an element of spots.
;;;   For every spot, spot, in spots
;;;     (spot.col leftmost) <= (spot.col spot)
(define spots.leftmost
(lambda (spots)
(if (null? (cdr spots))
(car spots)
(spot.leftmost (car spots) (spots.leftmost (cdr spots))))))
```

The definition of `spots.leftmost` includes a call to the `spot.leftmost` procedure, which returns the leftmost of two spots.

```;;; Procedure:
;;;   spot.leftmost
;;; Parameters:
;;;   spot1, a spot
;;;   spot2, a spot
;;; Purpose:
;;;   Finds the leftmost of the two spots.
;;; Produces:
;;;   leftmost, a spot
;;; Preconditions:
;;; Postconditions:
;;;   leftmost is spot1 or spot2.
;;;   (spot.col leftmost) <= (spot.col spot1)
;;;   (spot.col leftmost) <= (spot.col spot2)
(define spot.leftmost
(lambda (spot1 spot2)
(if (<= (spot.col spot1) (spot.col spot2))
spot1
spot2)))
```

Using a `let` expression, revise the definition of `spots.leftmost` so that the name `spot.leftmost` is bound to the procedure that it denotes only locally.

### Extra 2: The Leftmost Spot, Revisited

Note that there are two ways to do the previous problem. You can nest the `lambda` within the `let`, as in

```(define spots.leftmost
(let (...)
(lambda (spots)
...)))
```

or you can nest the `let` within th e`lambda`, as in

```(define spots.leftmost
(lambda (spots)
(let (...)
...)))
```

a. Define `spots.leftmost` in whichever way that you did not define it for the previous exercise.

b. Does the order of nesting affect what happens when the procedure is invoked?

c. If there is a difference, which arrangement is better? Why?

## Notes

### Notes on Problem 6b

Here are some duplicated expressions. You may have noted others.

• `(- (image.width image) 1)`
• `(position.col pos)`
• `(position.row pos)`
• `(/ 1 (- (image.width image) 1))`
• `(- width (position.col pos) 1)`

The following expressions are not duplicated. Nonetheless, you will find it useful to name them. You will be asked to do so in a later step of this exercise.

• `(rgb.red start-color)`
• `(rgb.red end-color)`
• `(rgb.green start-color)`
• `(rgb.green end-color)`
• `(rgb.blue start-color)`
• `(rgb.blue end-color)`

### Notes on Problem 6d

```(define image.fill-with-blend!
(lambda (image start-color end-color)
(let* ((width (image.width image))
(last-col (- width 1))
(frac (/ 1 last-col)))
(region.compute-pixels!
image
0 0
last-col (- (image.height image) 1)
(lambda (pos)
(rgb.new (+ (* (position.col pos)
frac
(rgb.red end-color))
(* (- width (position.col pos) 1)
frac
(rgb.red start-color)))
(+ (* (position.col pos)
frac
(rgb.green end-color))
(* (- width (position.col pos) 1)
frac
(rgb.green start-color)))
(+ (* (position.col pos)
frac
(rgb.blue end-color))
(* (- width (position.col pos) 1)
frac
(rgb.blue start-color)))))))))
```

### Notes on Problem 6g

```(define image.fill-with-blend!
(lambda (image start-color end-color)
(let* ((width (image.width image))
(last-col (- width 1))
(frac (/ 1 last-col)))
(region.compute-pixels!
image
0 0
last-col (- (image.height image) 1)
(lambda (pos)
(let* ((col (position.col pos))
(loc (- width col 1)))
(rgb.new (+ (* col frac (rgb.red end-color))
(* loc frac (rgb.red start-color)))
(+ (* col frac (rgb.green end-color))
(* loc frac (rgb.green start-color)))
(+ (* col frac (rgb.blue end-color))
(* loc frac (rgb.blue start-color))))))))))
```

### Notes on Problem 6j

```(define image.fill-with-blend!
(lambda (image start-color end-color)
(let* ((width (image.width image))
(last-col (- width 1))
(frac (/ 1 last-col))
(start-red (rgb.red start-color))
(end-red (rgb.red end-color))
(start-green (rgb.green start-color))
(end-green (rgb.green end-color))
(start-blue (rgb.blue start-color))
(end-blue (rgb.blue end-color)))
(region.compute-pixels!
image
0 0
last-col (- (image.height image) 1)
(lambda (pos)
(let* ((col (position.col pos))
(loc (- width col 1)))
(rgb.new (+ (* col frac end-red)
(* loc frac start-red))
(+ (* col frac end-green)
(* loc frac start-green))
(+ (* col frac end-blue)
(* loc frac start-blue)))))))))
```

## History

Disclaimer: I usually create these pages on the fly, which means that I rarely proofread them and they may contain bad grammar and incorrect details. It also means that I tend to update them regularly (see the history for more details). Feel free to contact me with any suggestions for changes.

This document was generated by Siteweaver on Mon Dec 3 09:55:04 2007.
The source to the document was last modified on Tue Oct 16 11:51:42 2007.
This document may be found at `http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2007F/Labs/let-lab.html`.

You may wish to validate this document's HTML ; ;

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright © 2007 Janet Davis, Matthew Kluber, and Samuel A. Rebelsky. (Selected materials copyright by John David Stone and Henry Walker and used by permission.) This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License. To view a copy of this license, visit `http://creativecommons.org/licenses/by-nc/2.5/` or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.