Skip to main content

Lab: Debugging your programs

Held
Friday, 8 March 2019
Writeup due
Monday, 11 March 2019
Summary
In this laboratory, we start to explore the DrRacket debugger.

Preparation

a. Make sure that the loudhum package is up to date.

b. Require the rackunit and rackunit/text-ui packages.

c. Add the following undocumented code to your definitions pane.

(define numbers '(3 7 8 6 16 19 12 0 4 5 17 15 11 13 1 9 14 2 10 18))

(define sum (section reduce + <>))
(define smallest (section reduce min <>))
(define largest (section reduce max <>))

(define second-smallest
  (lambda (numbers)
    (list-ref (sort numbers <) 2)))

(define second-largest
  (lambda (numbers)
    (list-ref (sort numbers >) 2)))

;;; Procedure:
;;;   average-w/o-extremes
;;; Parameters:
;;;   numbers, a nonempty list of real numbers
;;; Purpose:
;;;   Computes the average of numbers after dropping the two highest and two lowest
;;; Produces:
;;;   avg, a real number
;;; Preconditions:
;;;   * (length numbers) is greater than 4
;;; Postconditions:
;;;   * If max1, max2, min1, min2, are the largest, second largest,
;;;     smallest, and second smallest values in numbers, respectively,
;;;     then avg is the average of all the values in numbers excluding
;;;     max1, max2, min1, min2
;;;   * If all values in numbers are exact, then avg is exact
;;;   * If at least one value in numbers is inexact, then avg is inexact
(define average-w/o-extremes
  (lambda (numbers)
    (/ (- (sum numbers)
          (smallest numbers)
          (second-smallest numbers)
          (largest numbers)
          (second-largest numbers))
       (- (length numbers) 4))))

;;; Procedure:
;;;   drop-to-first-zero
;;; Parameters:
;;;   lst, a list of numbers
;;; Purpose:
;;;   Removes all of the elements up to and including the first zero.
;;; Produces:
;;;   newlst, a list of numbers
;;; Preconditions:
;;;   The list contains at least one zero.
;;; Postconditions:
;;;   Suppose the first zero is at index z.
;;;     (length newlst) = (- (length lst) z 1)
;;;     For all i s.t. z < i < (length lst)
;;;       (list-ref newlst (- i z 1)) = (list-ref lst z)
(define drop-to-first-zero
  (lambda (lst)
    (drop lst
          (index-of 0 lst))))

;;; Procedure:
;;;   remove-below
;;; Parameters:
;;;   num-lst, a list of real numbers
;;;   threshold, a real number
;;; Purpose:
;;;   Remove all numbers strictly below threshold from the list.
;;; Produces:
;;;   newlst, a list of real numbers
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   The numbers below threshold have been dropped.  That is, newlst contains
;;;     no number x such that x < threshold.
;;;   All other numbers have been retained.  That is, if x appears k times in
;;;     lst and x >= threshold, then x appears k times in newlst.
;;;   No additional numbers are in newlst.  That is, if x appears k times in
;;;     newlst then x appears k times in lst.
(define remove-below
  (lambda (num-lst threshold)
    (map (section + <> threshold)
         (drop-to-first-zero
          (map (section - <> threshold)
               (sort (append (list threshold) num-lst) <))))))

;;; Procedure:
;;;   remove-above
;;; Parameters:
;;;   num-lst, a list of real numbers
;;;   threshold, a real number
;;; Purpose:
;;;   Remove all numbers strictly above threshold from the list.
;;; Produces:
;;;   newlst, a list of real numbers
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   The numbers above threshold have been dropped.  That is, newlst contains
;;;     no number x such that x > threshold.
;;;   All other numbers have been retained.  That is, if x appears k times in
;;;     lst and x <= threshold, then x appears k times in newlst.
;;;   No additional numbers are in newlst.  That is, if x appears k times in
;;;     newlst then x appears k times in lst.
(define remove-above
  (lambda (num-lst threshold)
    (map (section + <> threshold)
     (drop-to-first-zero
      (map (section - <> threshold)
           (sort (append (list threshold) numbers) >))))))

;;; Procedure:
;;;   filter-range
;;; Parameters:
;;;   num-lst, a list of real numbers
;;;   lower, a real number
;;;   upper, a real number
;;; Purpose:
;;;   Removes all numbers strictly below lower and strictly above upper from the list.
;;; Produces:
;;;   newlst, a list of real numbers
;;; Preconditions:
;;;   [No additional]
;;; Postconditions:
;;;   The numbers outside the range have been dropped.  That is, newlst contains
;;;     no number x such that x < lower or x > upper.
;;;   All other numbers have been retained.  That is, if x appears k times in
;;;     lst and lower <= x <= upper, then x appears k times in newlst.
;;;   No additional numbers are in newlst.  That is, if x appears k times in
;;;     newlst then x appears k times in lst.
(define filter-range
  (lambda (num-lst lower upper)
    (remove-below
     (remove-above num-lst upper)
     lower)))

Exercises

Exercise 1: Exploring values of expressions

a. Suppose we were to call (average-w/o-extremes '(5 -10 1 2 -11 4 3 22 12)). What expressions do you expect to be evaluated, and in what order? What values will they have?

b. Click the Debug button.

c. Click the Go button.

d. Right-click (or control-click) on the open parenthesis that begins the body of average-w/o-extremes. (The body is the line after the parameter and begins with (/.) Select “Pause at this point.”

e. In the interactions pane, type (average-w/o-extremes '(5 -10 1 2 -11 4 3 22 12)) and then hit Enter. If all goes well, the program will stop at the point you added a pause.

f. Using the Step button, see what order DrRacket follows in the evaluation.

g. When the green triangle and green dot overlap, right click on the triangle and select “Print return value to console”. This allows you to explore what values expressions evaluate to and even change them with “Change return value…”

h. Summarize what information is and is not available while you step through the evaluation of the expression.

Exercise 2: Identifying potential problems

a. Write a test suite for average-w/o-extremes. Your test suite should be sufficiently robust that it is likely to identify an error in most incorrect implementations of average-w/o-extremes.

(define average-w/o-extremes-tests
  (test-suite
   "tests of average-w/o-extremes"
   (test-case "lists of size 7"
              (check-= (average-w/o-extremes (list 1 2 2 3 4 4 5)) 3 0)
              (check-= (average-w/o-extremes (list 2 2 2 5 8 8 8)) 5 0))
   ...))

b. Run your test suite on the code provided at the beginning of the lab

> (run-tests average-w/o-extremes-tests)

c. If your test suite passes all the tests, add the following check to your test suite and run it again.

(check-= (average-w/o-extremes (list 1 2 5 5 5 6 7)) 5 0)

d. Using the debugger, figure out what is wrong with the definition of average-w/o-extremes by using one of the failed inputs, stepping through the code and repeatedly predicting what should happen next, until one of your predictions fails to match. Printing the results of intermediate expressions may help.

e. Correct the implementation of average-w/o-extremes.

f. Describe the corrections you made.

Exercise 3: Other types of problems

a. Familiarize yourself with the documentation for drop-to-first-zero, remove-below, remove-above, and filter-range.

b. Execute (filter-range numbers 5 15) in the interactions pane to verify that it filters out all numbers outside of the range 5 to 15. Does it give you the output you expect?

c. Notice that filter-range duplicated the lower and upper values in the list of numbers. Step through the code using the debugger to identify which procedure is responsible for the error and fix it.

d. Write a few more tests for filter-range using your own lists and bounds and run them.

e. If you did not find any errors, consider the case (filter-range (list) 15 20). Step through the code using the debugger to identify which procedure is responsible for the error and fix it.

f. Describe what corrections you made (in both part c or part e).

Exercise 4: Debugger-free debugging strategies

Discuss with your partner each of the following strategies for finding problems in code. Which do you use? In what situations do you find it most helpful?

a. When dealing with a procedure over lists, first try it on the empty list, then on a few singleton lists, then on a few two-element lists, then on a few three-element lists, and so on and so forth.

b. Work out what the code should do on paper.

c. Rewrite the procedure again and compare your old answer to your new answer.

d. Make a list of inputs on which it fails and see if you can identify a common problem, then look to see what in the code relates to that problem.

e. Compare inputs for which it succeeds to inputs for which it fails and see how they differ. Then see where that difference might appear in the code.

Exercise 5: Other strategies

Make a list with your partner of other strategies you use when looking for problems in your code.

For those with extra time

We have not yet identified activities for those of you with extra time. You may depart early, but quietly.