- Held
- Friday, 22 February 2019
- Writeup due
- Monday, 25 February 2019
- Summary
- In this laboratory, you will ground your understanding of the basic
techniques for locally naming values and procedures in Scheme,
`let`

and`let*`

.

a. Start DrRacket.

b. Make sure that you have the latest version of the `loudhum`

package
by opening a terminal window and typing `/home/rebelsky/bin/csc151/update`

.
(Alternately, select **File** > **Install Package…**, enter
“`https://github.com/grinnell-cs/loudhum.git`

” and follow the instructions.)

c. Don’t forget to add `(require loudhum)`

to the definitions pane.

d. If we did not review the self checks at the start of class, review the self checks with your partner.

`let`

expressionsa. Write a *nested* `let`

-expression that binds a total of five
names, `alpha`

, `beta`

, `gamma`

, `delta`

, and `epsilon`

. With
`alpha`

bound to the value 7, and each subsequent value to twice
the previous value. That is, `beta`

should be 2*7, `gamma`

should
be two times that , and so on and so forth. The body of the innermost
`let`

should make a list from those values.

Your result will look something like

```
(let ([___ ___])
(let ([___ ___])
(let ([___ ___])
(let ([___ ___])
(let ([___ ___])
(list alpha beta gamma delta epsilon))))))
```

b. Write a similar expression, this time with `alpha`

bound to `1/3`

.
The remaining names should still be bound to subsequently doubled versions
of `alpha`

.

`let`

expressionsa. Write a `let*`

-expression equivalent to the `let`

-expression in the
previous exercise.

b. Since we’re repeating similar actions, it seems like building this
list of five elements is a natural candidate for using `map`

rather
than `let`

. Sketch what such a command would look like and compare and
contrast the two solutions. (By “sketch”, we mean write an outline of
the code, or describe a solution in pictures or English. You don’t need
to write working Scheme code.)

It is likely that you came up with a solution to part a of the prior exercise that looks something like the following.

```
(let* ([alpha 1/3]
[beta (* 2 alpha)]
[gamma (* 2 beta)]
...)
(list alpha beta gamma delta epsilon))
...)))
```

What if you decided that instead of doubling each previous value, you wanted to add three to that value? You’ll have four different expressions to change, which seems inefficient (at least in terms of programmer workload).

a. Rewrite the expression to use the name `fun`

for what needs to be done
to each element of the list. Your expression should look something
like the following.

```
(let* ([fun (section * <> 2)]
[alpha 1/3]
[beta (fun alpha)]
...)
...)
```

or

```
(let* ([fun (lambda (x) (* 2 x))]
...)
...)
```

b. Change your code so that `alpha`

starts at 1 and `fun`

divides its
parameter by 3. What result do you expect?

c. Check your answer experimentally.

Bindings happen behind the scenes. It may, however, be useful to
see what bindings DrRacket is doing. The `loudhum`

package includes
a set of operations for viewing what happens when you do a binding:
`verbose-define`

, `verbose-let`

, and `verbose-let*`

. Since you will
rarely need these procedures, we don’t include them in the primary
`loudhum`

package. Hence, you need to require them separately.

a. Add the following line to your definitions pane.

```
(require loudhum/verbose-bindings)
```

b. Rewrite the examples from Exercise 1 to use `verbose-let`

.

c. Rewrite your code from Exercise 2 to use `verbose-let`

.

d. Rewrite your code from Exercise 3 to use `verbose-let*`

.

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
(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
(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))))
```

a. Rename the first version `years-to-seconds-a`

and the second
`years-to-seconds-b`

.

b. In both versions, replace `let*`

with `verbose-let*`

.

c. Confirm that `years-to-seconds-a`

does, in fact,
recompute the values each time it is called.

d. Confirm that `years-to-seconds-b`

does *not* recompute the
values each time it is called.

e. Given that `years-to-seconds-b`

does not recompute each time, when
does it do the computation? (Consider when you see the messages.)

`define`

expressionsa. There are two examples related to nested `define`

expressions in the
reading, entitled `sample-w/let`

and `sample-w/define`

. Copy them into
your definitions pane and confirm that they work as described.

b. Note that you may find it useful to mouse over the various copies of
`x`

to see where they refer.

c. Consider the following procedure.

```
(define sample2
(lambda (x)
(list x
(let ([x (+ x 1)])
(list x)))))
```

What do you expect the output of `(sample2 10)`

to be?

d. Check your answer experimentally.

e. Consider the following definition.

```
(define sample3
(lambda (x)
(list x
(let ([x (+ x 1)]
[x (+ x 1)])
(list x)))))
```

What do you expect the output of `(sample3 10)`

to be?

f. Check your answer experimentally.

g. Consider the following definition.

```
(define sample4
(lambda (x)
(list x
(let* ([x (+ x 1)]
[x (+ x 1)])
(list x)))))
```

What do you expect the output of `(sample4 10)`

to be?

h. Check your answer experimentally.

*If you find that you have extra time, you may choose to do one
or more of the following exercises.*

a. As you may have noted, one of the problems with regular
use of anonymous procedures (those that we don’t name with `define`

)
is that they can be a bit hard to read. Compare

```
(filter (section regexp-match-exact? "[A-Z][a-z]*" <>) '("Neither" "Sam" "nor" "I" "can" "think" "of" "a" "good" "example."))
```

to

```
(let ([proper-name? (section regexp-match-exact? "[A-Z][a-z]*" <>)])
(filter proper-name? '("Neither" "Sam" "nor" "I" "can" "think" "of" "a" "good" "example.")))
```

b. Find two other times we’ve written non-obvious sections (or lambdas or compositions) and use a similar technique to name them.

Consider the following procedures.

```
(define image-offset
(lambda (image hoff voff)
(overlay/offset (rectangle (image-width image) (image-height image) 0 'white)
hoff voff
image)))
(define image-series
(lambda (drawing)
(let ([f (section image-offset <> 0 15)])
(let ([d0 drawing])
(let ([d1 (f d0)])
(let ([d2 (f d1)])
(let ([d3 (f d2)])
(let ([d4 (f d3)])
(let ([d5 (f d4)])
(beside d0 d1 d2 d3 d4 d5))))))))))
```

a. Explain, in your own words, what image-series does.

b. Check your answer experimentally.

c. Rewrite the expression to be more concise.

Most of this lab is taken nearly verbatim from a similar lab from spring 2018.

The two extras are based, in part, from another similar lab from spring 2017.