%CustomEntities; %CourseEntities; %CommonEntities; ]>
Laboratory: Local Procedures and Recursion Summary: In this laboratory, we consider the various techniques for creating local recursive procedures, particularly letrec and named let. We also review related issues, such as husk-and-kernel programming.
Reference A letrec statement has the form (letrec ((name-of-recursive-proc body) (name-of-recursive-proc body) ... (name-of-recursive-proc body)) expression expression ... expression) This statement builds a new environment (that maps names to values), adds all of the named procedures to the environment, evaluates the expressions and then forgets about the environment. It is useful for temporarily creating local procedures, and for limiting access to kernels. A named let is an alternative mechanism for defining recursive kernels. It can be used only when you want to define a single recursive kernel and call it immediately, and has the form (let kernel ((name_1 exp_1) ... (name_n exp_n)) body) The named let tells Scheme to define a new procedure (whose name is kernel), with parameters that correspond to the names and whose body corresponds to body. That is, it creates the following procedure (let ((kernel (name_1 ... name_2) body)) ...) It then gives the result of of calling kernel on exp_1 ... exp_n.
Preparation a. If any of the following procedures are not in your library, add them to your library. b. Create a new window in which you load your library file.
Exercises
Exercise 1: The Brightest Color, Revisited As the reading suggested, local kernels are particularly appropriate for checking preconditions. Local kernels are also appropriate for writing recursive helper procedures that take an extra parameter to accumulate a result. For example, here's a helper-based definition of rgb-brightest. a. Rewrite this to make the helper local and to name the helper kernel. b. Test your procedure on a few simple lists of colors. For example, you might try some of the following. > (rgb->color-name (rgb-brightest (map color-name->rgb (list "red" "green" "blue")))) > (rgb->color-name (rgb-brightest (map color-name->rgb (list "black")))) > (rgb->color-name (rgb-brightest (map color-name->rgb (list "red" "black" "green" "yellow")))) > (rgb->color-name (rgb-brightest (map color-name->rgb (list "yellow" "red" "black" "green")))) c. When you are done, you may want to compare your answer to the sample solution in the notes on this problem.
Exercise 2: Safely Computing the Brightest Color The procedure given above, and your rewrite of that procedure, will fail miserably if given an inappropriate parameter, such as an empty list, a list that contains values that are not colors, or a non-list. Rewrite rgb-brightest so that it checks its preconditions (including that the list contains only RGB colors) before calling the kernel. Note that you can use rgb? to check if a value represents an RGB color.
Exercise 3: Computing the Brightest Color, Revisited Again Rewrite rgb-brightest-helper using a named let rather than letrec. (The goal of this problem is to give you experience using named let, so you need not check preconditions for this exercise.) When you are done, you may want to compare your answer to the sample solution in the notes on this problem.
Exercise 4: Taking Some Elements Define and test a procedure, (list-take lst n), that returns a list consisting of the first n elements of the list, lst, in their original order. You might also think of list-take as returning all the values that appear before index n. For example, > (list-take (list "a" "b" "c" "d" "e") 3) ("a" "b" "c") > (list-take (list 2 3 5 7 9 11 13 17) 2) (2 3) > (list-take (list "here" "are" "some" "words") 0) () > (list-take (list null null) 2) (() ()) > (map rgb->color-name (list-take (map color-name->rgb (list "black" "white" "green") 1)) ("black") The procedure should signal an error if lst is not a list, if n is not an exact integer, if n is negative, or if n is greater than the length of lst. Note that in order to signal such errors, you may want to take advantage of the husk-and-kernel programming style. Use named let or letrec.
For Those With Extra Time
Extra 1: Taking Some More Elements Rewrite list-take to use whichever of named let and letrec you didn't use in the previous exercise.
Extra 2: Reflection You've now seen two examples in which you've written two different solutions, one using letrec and one use named let. Reflect on which of the two strategies you prefer and why.
Extra 3: Alternating Lists A list of spots is a bright-dark-alternator if its elements are alternately bright spots and dark spots, beginning with a bright spot. A list of spots is a dark-bright-alternator if its elements are alternately dark and bright, beginning with a dark spot. (We say that the empty list is both a bright-dark-alternator and a dark-bright-alternator.) a. Write a predicate, spots-alternating-brightness?, that determines whether a non-empty list of spots is either a bright-dark-alternator or a dark-bright-alternator. Your definition should include a a letrec expression in which the identifiers bright-dark-alternator? and dark-bright-alternator? are bound to mutually recursive predicates, each of which determines whether a given list has the indicated characteristic. (define spots-alternating-brightness? (lambda (spots) (letrec ((bright-dark-alternator? (lambda (spots) ...)) (dark-bright-alternator? (lambda (spots) ...))) ...))) When you are done, you may want to compare your answer to the sample solution in the notes on this problem. b. Here are a few lists of spots. For which do you expect spots-alternating-brightness? to hold? (define white (rgb-new 255 255 255)) (define black (rgb-new 0 0 0)) (define red (rgb-new 255 0 0)) (define green (rgb-new 0 255 0)) (define blue (rgb-new 0 0 255)) (define yellow (rgb-new 255 0 0)) (define sample0 null) (define sample1 (list (spot-new 0 0 white))) (define sample2 (list (spot-new 0 0 black))) (define sample3 (list (spot-new 0 0 white) (spot-new 0 1 black))) (define sample4 (list (spot-new 0 0 black) (spot-new 0 1 white))) (define sample5 (list (spot-new 0 0 black) (spot-new 0 1 black))) (define sample6 (list (spot-new 0 0 white) (spot-new 0 1 black) (spot-new 0 2 white))) (define sample7 (list (spot-new 0 0 white) (spot-new 0 1 black) (spot-new 0 2 white) (spot-new 0 3 black) (spot-new 0 4 white))) (define sample8 (list (spot-new 0 0 red) (spot-new 1 0 yellow) (spot-new 2 0 blue) (spot-new 3 0 white) (spot-new 4 0 black))) c. Check your answers experimentally.
Notes
Notes on Exercise 1: The Brightest Color, Revisited Here's one possible solution.
Notes on Exercise 3: Computing the Brightest Color, Revisited Again Here's one possible solution.
Notes on Extra 3: Alternating Lists Once bright-dark-alternator? and dark-bright-alternator? are written, the definition is fairly straightforward. A non-empty list of spots has alternating brightness if it's not empty and it's either a bright-dark-alternator or a dark-bright-alternator. (and (not (null? spots)) (or (bright-dark-alternator? spots) (dark-bright-alternator? spots))) Each definition is also fairly straightforward. A list is a bright-dark-alternator if it is empty or if the car is bright and the cdr is a dark-bright-alternator. (bright-dark-alternator? (lambda (spots) (or (null? spots) (and (rgb-bright? (spot.color (car spots))) (dark-bright-alternator? (cdr spots)))))) The definition of dark-bright-alternator? is so similar that we will not provide it separately. Putting everything together, we get Note that there's a hidden moral here: The procedures defined in a letrec can be mutually recursive.