Skip to main content

Laboratory: Recursion with Helper Procedures

Summary: In this laboratory, you will explore recursive techniques in which we pass along intermediate computations, often using a recursive helper procedure. When this technique is used in conjunction with a program structure in which the recursive result is returned directly (without accumulated actions to perform), this technique supports tail recursion.

Preparation

a. Make a copy of helper-recursion-lab.rkt, which contains the definitions from the reading, as well as a few other procedures.

b. Create a list of twelve or so RGB colors and call it my-colors. For example,

(define my-color-names 
  (list "palevioletred" "cadetblue" "darkseagreen" "goldenrod"
        "hotpink" "lightsteelblue" "burlywood" "mistyrose"
        "salmon" "peru" "plum" "turquoise"))
(define my-colors
  (map color-name->irgb my-color-names))

c. Create a few lists of shades of grey as follows:

(define greys-4
  (map (lambda (n) (irgb (* 64 n) (* 64 n) (* 64 n)))
       (list 4 3 2 1)))
(define greys-8
  (map (lambda (n) (irgb (* 32 n) (* 32 n) (* 32 n)))
       (list 8 7 6 5 4 3 2 1)))
(define greys-16
  (map (lambda (n) (irgb (* 16 n) (* 16 n) (* 16 n)))
       (list 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1)))

Exercises

Exercise 1: Product, Revisited

a. Rewrite the product procedure, which computes the product of a list of values, using the same technique used for new-sum.

b. Write a similar my-quotient procedure. (Do not call it quotient, which is a built-in procedure that is commonly used.)

Exercise 2: Summing Red Components, Revisited

In the previous lab on recursion, you wrote a procedure, sum-red, that sums all the red components in a list of colors. Rewrite that procedure using the technique of carrying along intermediate values. Your procedure should look something like the following:

(define sum-red
  (lambda (colors)
     (sum-red-helper 0 colors)))
(define sum-red-helper 
  (lambda (sum-so-far remaining-colors)
    ...))

Exercise 3: Filtering, Revisited

a. Here’s a list of colors. Add it to your definitions pane.

(define more-colors
  (map color-name->irgb (list "red" "green" "blue" "yellow" "black" "white")))

b. Print out the RGB values in both your list of colors and this new list of colors with

> (map irgb->string my-colors)
> (map irgb->string more-colors)

c. Here’s a procedure using the “results so far” technique that filters out any colors with a red component of at least 128. (You can also find a copy of this procedure in the code accompanying this lab.)

(define irgb-filter-out-high-red
  (lambda (colors)
    (irgb-filter-out-high-red-helper null colors)))
(define irgb-filter-out-high-red-helper
  (lambda (colors-so-far remaining-colors)
    (cond
      [(null? remaining-colors)
       colors-so-far]
      [(<= 128 (irgb-red (car remaining-colors)))
       (irgb-filter-out-high-red-helper colors-so-far (cdr remaining-colors))]
      [else
       (irgb-filter-out-high-red-helper
         (cons (car remaining-colors) colors-so-far)
         (cdr remaining-colors))])))

What do you expect this procedure to do when given the empty list?

d. Check your answer experimentally.

e. What do you expect this procedure to do when given the list of the color white?

> (irgb-filter-out-high-red (list (color->irgb "white")))

f. Check your answer experimentally.

g. What do you expect this procedure to do when given the list of the color black?

> (irgb-filter-out-high-red (list (color->irgb "black")))

h. Check your answer experimentally.

i. What do you expect this procedure to do when given the singleton list of the color blue?

> (irgb-filter-out-high-red (list (color->irgb "blue")))

j. Check your answer experimentally.

k. What do you expect this procedure to do when given the list of the colors we created at the beginning of this exercise?

> (irgb-filter-out-high-red more-colors)

l. Check your answer experimentally.

m. What do you expect this procedure to do when given your list of colors as a parameter?

(map irgb->string (irgb-filter-out-high-red my-colors))

n. Check your answer experimentally.

o. In at least one case above, you should have received a somewhat strange result. Do your best to explain that result. If you’re not sure, check the notes on this problem. Then, fix the code so that the result is not so strange.

Exercise 4: Counting Steps

a. Let’s use the ideas from the reading on output in Scheme to count steps in the various versions of irgb-brightest by having the procedure display some output for each step. We’ll start with the first version.

(define irgb-brightest
  (lambda (colors)
    (cond
      [(null? (cdr colors))
       (car colors)]
      [(>= (irgb-brightness (car colors)) 
           (irgb-brightness (irgb-brightest (cdr colors))))
       (car colors)]
      [else
       (irgb-brightest (cdr colors))])))

Add the following line immediately before the cond statement (that is, as the first line).

(display "irgb-brightest on list length ") (display (length colors)) (newline)

b. How many lines do you expect that the program to print in the following call?

> (irgb-brightest greys-4)

c. Check your answer experimentally.

d. How many lines do you expect that the program to print in the following call?

> (irgb-brightest greys-8)

e. Check your answer experimentally.

f. How many lines do you expect that the program to print in the following call?

> (irgb-brightest greys-16)

g. Check your answer experimentally.

h. How many lines do you expect that the program to print in the following call?

> (define grays-4 (reverse greys-4))
> (irgb-brightest grays-4)

i. Check your answer experimentally.

j. How many lines do you expect that the program to print in the following call?

> (define grays-8 (reverse greys-8))
> (irgb-brightest grays-8)

k. Check your answer experimentally.

l. Explain why we didn’t have you try the same steps again, using a reversed list of sixteen greys.

Exercise 5: Counting More Steps

a. Repeat the previous exercise using the helper version of rgb-brightest. Put the calls to display in the recursive helper procedure. (Note that you may have to change the call to display slightly.)

(define irgb-brightest
  (lambda (colors)
    (irgb-brightest-helper (car colors) (cdr colors))))
(define irgb-brightest-helper
  (lambda (brightest-so-far colors-remaining)
    (if (null? colors-remaining)
        brightest-so-far
        (irgb-brightest-helper
         (if (>= (irgb-brightness brightest-so-far) 
                 (irgb-brightness (car colors-remaining)))
             brightest-so-far
             (car colors-remaining))
         (cdr colors-remaining)))))

b. What do your results suggest to you about this new version of irgb-brightest as compared to the prior version?

For Those With Extra Time

Extra 1: Summing Multiple Components, Simultaneously

Although we’ve primarily used helpers to keep track of one intermediate result, we can certainly pass along more than one intermediate result. For example, in averaging a list of colors, we can keep track of the sum of reds, the sum of greens, the sum of blues, and the count of colors. In the end, we can build a new color from these computed values.

(define irgb-average
  (lambda (colors)
    (irgb-average-helper 0 0 0 0 colors)))
(define irgb-average-helper
  (lambda (red-so-far green-so-far blue-so-far count remaining-colors)
    (if (null? remaining-colors)
        (irgb (/ red-so-far count)
                 (/ green-so-far count)
                 (/ blue-so-far count))
        (irgb-average-helper ...))))

a. Fill in the remaining code in the recursive call.

b. Test this code to average white and black.

(irgb->string (irgb-average (list (irgb 0 0 0) (irgb 255 255 255))))

c. Test this code on a few other colors of your choice.

d. What do you expect to have happen if you provide irgb-average with the empty list?

e. Check your answer experimentally.

–>

Extra 2: Too Many Ways to Compute the Brightest Color

The reading provides at least four ways to compute the brightest color in a list. Which do you prefer, and why?

Extra 3: Yet Another Way To Compute the Brightest Color

Let’s consider the first version of rgb-brightest.

(define irgb-brightest
  (lambda (colors)
    (if (null? (cdr colors))
        (car colors)
        (if (>= (irgb-brightness (car colors)) 
                (irgb-brightness (irgb-brightest (cdr colors))))
            (car colors)
            (irgb-brightest (cdr colors))))))

Suppose we used let to bind names to the car and the result of the recursive call

(let ([candidate (irgb-brightest (cdr colors)])
      [alternate (car colors)])
  ...)

a. Rewrite this version of irgb-brightest to use these names in place of the values they name.

b. What effect do you expect this naming to have on the efficiency of the procedure?

c. Check your answer experimentally.

Notes on the Exercises

Notes on Exercise 3: Filtering, Revisited

As you may have noticed, the results are presented in reverse order. That is, the black at the end of more-colors is at the front of the list that results from filtering. Ideally, the values should appear in the same order in the result.

Return to the problem.