Functional Problem Solving (CSC 151 2016S) : Assignments

Exam 3: Recursion


Assigned: Wednesday, 6 April 2016

Due: The due dates for various tasks are as follows.

Important! This examination has an initial code file that you should copy and use as the starting point for your work.

Please read the information on this examination, which includes both instructions and a checklist.

Problems

Problem 1: Whatzitdo?

Topics: Recursion over lists, named let, code reading, documentation

Consider the following interesting procedure definition.

(define s
  (lambda (l)
    (let kernel ([a null]
                 [b null]
                 [c l])
      (cond
        [(null? c)
         (list (reverse a) (reverse b))]
        [(number? (car c))
         (kernel (cons (car c) a) b (cdr c))]
        [else
         (kernel a (cons (car c) b) (cdr c))]))))

Figure out what this program does and then do the following.

a. Rename the procedure and the parameters so that they will make sense to the reader.

b. Explain why there are two calls to reverse in the base case.

c. Write 6P-style documentation for the code.

Problem 2: The nearest color

Topics: Integer-encoded RGB colors, list recursion

We've seen in the past that a simple metric for the “distance” between two colors is the sum of the squares of the differences between their individual components.

;;; Procedure:
;;;   irgb-distance
;;; Parameters:
;;;   color1, an integer-encoded RGB color
;;;   color2, an integer-encoded RGB color
;;; Purpose:
;;;   Find the distance between color1 and color2, using some simple
;;;   metric for distance.
;;; Produces:
;;;   distance, a non-negative real number
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   If color1=color2, then distance is 0
;;;   For any three colors, a, b, and c, if 
;;;     (irgb-distance a b) < (irgb-distance b c)
;;;   then a is likely to be perceived as being closer to b than c.
;;; Plus:
;;;   irgb-distance is commutative.  That is, for any two colors, a and b,
;;;     (irgb-distance a b) = (irgb-distance b a)
(define irgb-distance
  (lambda (color1 color2)
    (+ (square (- (irgb-red color1) (irgb-red color2)))
       (square (- (irgb-green color1) (irgb-green color2)))
       (square (- (irgb-blue color1) (irgb-blue color2))))))

Once we have such a metric, it's reasonable to ask which color in a list is “closest” to a target color. (That is, which color has the least distance to color.) In a recent examination, you saw a procedure that found which of four colors was closest to an initial color. Now that we know recursion, it seems like we should be able to do better. In particular, we might find the element of a list which is closest to an original color. Here's documentation for such a procedure.

;;; Procedure:
;;;   irgb-closest
;;; Parameters:
;;;   original, an integer-encoded RGB color
;;;   colors, a non-empty list of integer-encoded RGB colors
;;; Purpose:
;;;   Find the element of colors that is "closest" to original.
;;; Produces:
;;;   closest, an integer-encoded RGB color
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   closest is a member of colors.
;;;   For all colors, c, in colors
;;;     (irgb-distance original closest) <= (irgb-distance original c)

Implement irgb-closest.

Problem 3: rac and rdc

Topics: Direct recursion over lists, core list operations

As you know, the car grabs the element at the front of the list the cdr grabs all but the element at the front of the list. Of course, there are times that it's useful to work from the back of the list, rather than the front. Hence, we might want a procedure to grab the last element (we'll call that rac) and a procedure to grab all but the last element (we'll call that rdc).

It is possible to implement both rac and rdc with a call to reverse. But that may be inefficient, since reverse builds a new list. It is also possible to implement rac using list-ref with an index of one less than the length of the list. But that's also inefficient, since we have to traverse the list twice, once to find its length, and once to extract the element.

So, what should we do? Not so surprisingly, we can implement both rac and rdc recursively.

Document and write recursive versions of rac and rdc. You should use direct recursion and should not use any helper procedures. (Note: “direct recursion” is what we have also called “basic recursion”.)

In developing your solution, you may certainly rely on car, cdr, cons, and null?. However, you may not rely on other list procedures, such as list-ref, list-take, or list-drop. (If you are unsure as to whether or not you can use a procedure, ask.)

Problem 4: Subtle substrings

Topics: Numeric recursion, strings, testing

It is often useful to determine whether one string appears within another string. For example, we might want to know whether a faculty member is a rebel by checking for the appearance of the word “rebel” in their name. Consider the following implementation of index-of-substring, which is supposed to achieve that purpose.

;;; Procedure:
;;;   index-of-substring
;;; Parameters:
;;;   str, a string
;;;   substr, a string
;;; Purpose:
;;;   Find the index of an instance of substr in str.
;;; Produces:
;;;   pos, an integer or #f
;;; Preconditions:
;;;   No additional.
;;; Postconditions:
;;;   If substr is not contained in str, then pos is #f
;;;   If substr is contained within str, then pos is a non-negative
;;;   integer and 
;;;     0 <= pos < (string-length str)
;;;     substr appears as a substring of str, with the initial character
;;;     of substr at position pos of str.  That is,
;;;     (equal? (substring str pos (+ pos (string-length substr))) substr)
;;;   If substr appears multiple times within str, then pos may indicate
;;;     any of the appearances.
(define index-of-substring
  (lambda (str substr)
    (let ([substr-len (string-length substr)])
      (let kernel ([pos 0]
                   [finish (- (string-length str) substr-len)])
        (cond 
          [(= pos finish)
           #f]
          [(equal? substr (substring str pos (+ pos substr-len)))
           pos]
          [else
           (kernel (+ 1 pos) finish)])))))

Here is a simple test suite that someone developed for this procedure.

(define contains-tests
  (test-suite
   "Tests of string-contains"
   (check-equal? (index-of-substring "banana" "ban") 
                  0 
                  "start of string")
   (check-equal? (index-of-substring "banana" "nan") 
                  2
                  "middle of string")
   (check-false (index-of-substring "banana" "NAN") 
                 "incorrect capitalization")
   (check-false (index-of-substring "banana" "orange")
                 "mismatched fruit")))

Although the procedure passes the given tests, it is not correctly implemented. In fact, there are at least two different situations in which this procedure gives an incorrect result.

a. Add two distinct checks for which the given implementation of index-of-substring fails.

What do we mean by distinct? The relationship between substr and str should be different in each case. As a counterexample, the following two tests are not distinct because they test essentially the same thing, a matching substring at the start of the string.

   (check-equal? (index-of-substring "banana" "ban") 
                  0)
   (check-equal? (index-of-substring "pineapple" "pine") 
                  0)

b. Once you have found additional errors in this implementation, develop a correct implementation. (You may find it useful to add even more tests.)

Problem 5: Stenciling a shape

Topics: Repetition (through numeric recursion), pixel-based images, randomness.

Consider the following algorithm for drawing a shape.

Repeat many times:

  1. Choose a random position in the image.
  2. If that position falls inside the shape, then color the pixel at that position.

We call this a Monte Carlo method; just like there is randomness at a casino, there is randomness in our method. You could think of this approach to drawing as like spray-painting through a stencil: each position inside the shape has some probability of being painted, but it's unpredictable which ones will actually be chosen.

Write, but do not document, a procedure, (stencil-quarter-circle radius n color). Your procedure should use the following algorithm.

Create an image of size radius x radius.
Do the following n times:
  Select a random column and row within the image.
  If the distance from that point to the origin is less than radius,
    then color the pixel using image-set-pixel!.
Produce the image.

The result should look something like this:

> (image-show (stencil-quarter-circle 100 5000 (irgb 0 0 0)))

Problem 6: Estimating pi

Topics: Numeric recursion, randomness

The technique you just used to “spray-paint” a quarter-circle forms the basis of a Monte Carlo method for estimating the value of pi.

Consider the quarter-circle with radius 1. We select N random points where both the x and y values fall between 0 and 1. As we go along, we count how many of those points fall within the quarter-circle; call this M.

Observe that M/N is equal to the ratio between the area of the quarter-circle and the area of the unit square. So, we estimate that pi is approximately M/N * 4, and produce this value.

The larger N is, the better our estimate will be.

Write and document a procedure, (estimate-pi n), which implements this algorithm.

In your sample output, show that providing larger values of n leads to increasingly precise estimates. For example:

> pi
3.141592653589793
> (estimate-pi 10)
3.6
> (estimate-pi 100)
3.0
> (estimate-pi 1000)
3.204
> (estimate-pi 10000)
3.1304
> (estimate-pi 100000)
3.1503
> (estimate-pi 1000000)
3.141568

Some Questions and Answers

Here we will post answers to questions of general interest. Please check here before emailing your questions!

General Questions

What is a general question?
A question that is about the exam in general, not a particular problem.
Do the two sections have the same exam?
More or less.
Can we still invoke the “There's more to life” clause if we spend more than five hours on the exam?
Yes. However, we really do recommend that you stop at five hours unless you are very close to finishing. It's not worth your time or stress to spend more effort on the exam. It is, however, worth your time to come talk to us, and perhaps to get a tutor or more help (not on this exam, but on the class). There's likely some concept you're missing, and we can help figure that out.
If we get more than 70 points, does the “There's more to life” clause drop our grade to 70?
No! The “There's more to life” clause provides a minimum grade for people who do appropriate amounts and type of work and provide evidence of some mastery.
What do you mean by “implement”?
Write a procedure or procedures that accomplish the given task.
Do we have to make our code concise?
You should strive for readable and correct code. If you can make it concise, that's a plus, but concision is secondary to readability and correctness. Long or muddled code is likely to lose points, even if it is correct.
Much of your sample 6P-style documentation has incomplete sentences. Can we follow that model? That is, can we use incomplete sentences in our 6P-style documentation?
Yes, you can use incomplete sentences in 6P-style documentation.
You tell us to start the exam early, but then you add corrections and questions and answers. Isn't that contradictory? Aren't we better off waiting until you've answered the questions and corrected any errors?
That's one of the reasons we give extra credit to those who work on the exam early. But you're also better able to get your questions answered early if you start early (or at least we think you are). Later questions will generally be told “See the notes on the exam”.
How do we know what our random number is?
You should have received one in class. If you need a new one, there's a stack in the back of our classroom.
To show we’ve tested the code informally, would you just like us to just post the inputs we used to test the procedure? If so, how should we list those?
Copy and paste the interactions pane into the appropriate place in the definitions pane. Select the text. Under the Racket menu, use "Comment out with semicolons."
Should we include examples and, if so, how do we include them?
You should certainly include examples. We would recommend that you copy and paste them from the interactions pane to right below the problem in the definitions pane, and then comment them out with semicolons. (Select and then choose Comment out with semicolons from the Racket menu. Do not use Comment out with a box!
Should we cite our partner from a past lab or assignment if we use code from a past lab or assignment?
You should cite both yourself and your partner, although you should do so as anonymously as possible. For example “Ideas taken from the solution to problem 7 on assignment 3 written by student 641321 and partner”.
What is this STUB comment that appears in the code file?
We typically use the term “STUB” to indicate that we've put in a piece of code to get the program to run, but that the code is intended only as a placeholder until we write something correct.
I know that you prefer that lines be under 80 characters. But what if I'm citing a Web page and the URL is longer than 80 characters?
In that particular case, it's fine that the line is longer than 80 characters.

Errata

Here you will find errors of spelling, grammar, and design that students have noted. Remember, each error found corresponds to a point of extra credit for everyone. We usually limit such extra credit to five points. However, if we make an astoundingly large number of errors, then we will provide more extra credit. (Sam's record is nine points of extra credit; Charlie's is about five.) Note that we do not count errors in the errata section, the question and answer sections, or the separate code file.

  • The exam was assigned on Wednesday, April 6, not Wednesday, April 7. [RJ, 1 point]
  • irgb-distance misspelled as irgb-distrance in documentation. [RJ, DL, 0 points]
  • The example use of stencil-quarter-circle incorrectly called rgb-new instead of irgb-new or irgb. [AG, 1 point]

Citations

Some of the problems on this exam are based on (and at times copied from) problems on previous exams for the course. Those exams were written by Charlie Curtsinger, Janet Davis, Rhys Price Jones, Samuel A. Rebelsky, John David Stone, Henry Walker, and Jerod Weinman. Many were written collaboratively, or were themselves based upon prior examinations, so precise credit is difficult, if not impossible.

Some problems on this exam were inspired by conversations with our students and by correct and incorrect student solutions on a variety of problems. We thank our students for that inspiration. Usually, a combination of questions or discussions inspired a problem, so it is difficult and inappropriate to credit individual students.

If there's a photograph of a kitten in the exam, that photograph was released for public use at http://public-photo.net/displayimage-2485.html. It appears that site is now defunct.