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
What are the values of the following
You may use DrScheme to help you answer these questions, but be sure you
can explain how it arrived at its answers.
(let ((tone "fa") (call-me "al")) (list call-me tone "l" tone))
(let ((total (+ 8 3 4 2 7))) (let ((mean (/ total 5))) (* mean mean)))
(let ((inches-per-foot 12) (feet-per-mile 5280)) (let ((inches-per-mile (* inches-per-foot feet-per-mile))) (* inches-per-mile inches-per-mile)))
Write a nested
let-expression that binds a total of five
bound to 9387 and each subsequent name bound to a value twice
as large as the one before it. That is,
beta should be
twice as large as
gamma twice as
beta, and so on. The body of the innermost
let-expression should then compute the sum of the values
of the five names.
let*-expression equivalent to the
let-expression in the previous exercise.
/home/rebelsky/Web/Courses/CS151/2006F/Examples/verbose-bindings.ss contains alternative versions of
a. Load this file.
b. Rewrite the examples from Exercise 1 to use
c. Rewrite your code from Exercise 2 to use
d. Rewrite your code from Exercise 3 to use
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
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))))
a. Confirm that
years-to-seconds-a does, in fact, recompute
the values each time it is called. (You might, for example, replace
b. Confirm that
years-to-seconds-b does not recompute the
values each time it is called. (Again, replace the
c. Given that
years-to-seconds-b does not recompute each
time, when does it do the computation? (Consider when you see the
You may recall that we defined a procedure to compute the roots of a quadratic polynomial as follows:
(define roots (lambda (a b c) (let ((negative-b (- b)) (square-root-of (sqrt (- (* b b) (* 4 a c)))) (two-a (* 2 a))) (list (/ (+ negative-b square-root-of) two-a) (/ (- negative-b square-root-of) two-a)))))
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
the lambda, as in
(define roots (let ((negative-b (- b)) (square-root-of (sqrt (- (* b b) (* 4 a c)))) (two-a (* 2 a))) (lambda (a b c) (list (/ (+ negative-b square-root-of) two-a) (/ (- negative-b square-root-of) two-a)))))
b. Explain, in your own words, why this fails (and why it should fail).
Local bindings are particularly useful when you are dealing with
input (and, at times, output). Recall that we regularly had to
call a helper procedure so that we could name values. For example,
we had to call
sum-of-file-helper-helper to name the
(define sum-of-file (lambda (filename) (sum-of-file-helper (open-input-file filename)))) (define sum-of-file-helper (lambda (source) (sum-of-file-helper-helper source (read source)))) (define sum-of-file-helper-helper (lambda (source nextval) (cond ((eof-object? nextval) (close-input-port source) 0) ((number? nextval) (+ nextval (sum-of-file-helper source))) (else (sum-of-file-helper source)))))
Now, we can name that value without calling the helper-helper.
(define sum-of-file-helper (lambda (source) (let ((nextval (read source))) (cond ((eof-object? nextval) (close-input-port source) 0) ((number? nextval) (+ nextval (sum-of-file-helper source))) (else (sum-of-file-helper source))))))
Verify that this update works. Recall that the file
contains a variety of numbers that sum to 258588.
Consider the following procedure that squares all the values in a list.
;;; Procedure: ;;; square-values ;;; Parameters: ;;; lst, a list of numbers of the form (num_1 num_2 ... num_n) ;;; Purpose: ;;; Squares all the values in lst. ;;; Produces: ;;; list-of-squares, a list of numbers ;;; Preconditions: ;;; [Standard] ;;; Postconditions: ;;; list-of-squares has the form (square_1 square_2 ... square_n) ;;; For all i, square_i is the square of num_i (that is num_i * num_i). (define square-values (lambda (lst) (let ((square (lambda (val) (* val val)))) (if (null? lst) null (cons (square (car lst)) (square-values (cdr lst)))))))
a. Verify that
square-values works correctly.
b. Try to execute
square outside of
Explain what happens.
Here is a procedure that takes a non-empty list of lists as an argument and returns the longest list in the list (or one of the longest lists, if there is a tie).
;;; Procedure: ;;; longest-list-in-list ;;; Parameters: ;;; los, a list of lists ;;; Purpose: ;;; Finds one of the longest lists in los. ;;; Produces: ;;; longest, a list ;;; Preconditions: ;;; los is a nonempty list. ;;; every element of los is a list. ;;; Postconditions: ;;; Does not affect los. ;;; Returns an element of los. ;;; No element of los is longer than longest. That is, ;;; For each lst in los, (length los) >= (length lst). (define longest-list-in-list (lambda (los) ; If there is only one list, that list must be the longest. (if (null? (cdr los)) (car los) ; Otherwise, take the longer of the first list and the ; longest remaining list. (longer-list (car los) (longest-list-in-list (cdr los))))))
This definition of the
includes a call to the
longer-list procedure, which returns
the longer of two given lists:
;;; Procedure: ;;; longer-list ;;; Parameters: ;;; left, a list ;;; right, a list ;;; Purpose: ;;; Find the longer of left and right. ;;; Produces: ;;; longer, a list ;;; Preconditions: ;;; Both left and right are lists. ;;; Postconditions: ;;; longer is a list. ;;; longer is either equal to left or to right. ;;; (>= (length longer) (length left)) ;;; (>= (length longer) (length right)) (define longer-list (lambda (left right) (if (<= (length right) (length left)) left right)))
Revise the definition of
longest-list-in-list so that the
longer-list is bound to the procedure that it denotes
only locally, in a
Note that there are at least two possible ways to do the previous exercise:
The definiens of
longest-list-in-list can be a
let-expression as its body, or it can be a
let-expression with a
lambda-expression as its
body. That is, it can take the form
(define longest-list-in-list (let (...) (lambda (los) ...)))
or the form
(define longest-list-in-list (lambda (los) (let (...) ...)))
longest-list-in-list 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? You may want to use
verbose-let to help you answer
c. If there is a difference, which arrangement is better? Why?
The two definitions you came up with in the previous exercises
are not the only alternatives you have in placing the
longer-list is only needed in the recursive case,
you can place the
(define longest-list-in-list (lambda (los) ; If there is only one list, that list must be the longest. (if (null? (cdr los)) (car los) ; Otherwise, take the longer of the first list and the ; longest remaining list. (let ((longer-list (lambda (left right) (if (<= (length right) (length left)) left right)))) (longer-list (car los) (longest-list -in-list (cdr los))))))
Including the original definition (in which
define), you've now seen or written four
longest-list-in-list. Which do you prefer?
Extend your favorite version of
that it verifies its preconditions (i.e., that
contains lists and that
los is nonempty). If the
preconditions are not met, your procedure should return
It is perfectly acceptable for you to check each list element in turn to determine whether or not it is a list, rather than to check them all at once, in advance.
Dr. Scheme provides a keyword,
time, that reports
various metrics for the time it takes to execute an expression.
(time exp) operation, determine
which of the four versions of
indeed the fastest.
In doing this testing, you should build a fairly long outer list. The inner lists can be mostly 0- or 1-element lists..
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 Thu Nov 30 21:43:02 2006.
The source to the document was last modified on Wed Sep 27 09:25:58 2006.
This document may be found at
You may wish to validate this document's HTML ; ;Samuel A. Rebelsky, email@example.com
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.