Documenting Your Procedures


Convention in Scheme (and all programming languages) is that we carefully document what our procedures do, including input values, output values, and assumptions. We use comments provide information to the reader of our program (that is, to people instead of the computer). In Scheme, comments begin with a semicolon and end with the end of the line.

There are a variety of kinds of comments we write. For now, we'll focus on the comments we write for the other programmers who might call the procedures we write.

Documenting the square Procedure

Let's consider the documentation that we wrote for the square procedure that was introduced in the first reading on procedures.

;;; Samuel A. Rebelsky and Janet Davis
;;; Department of Computer Science
;;; Grinnell College
;;; {rebelsky,davisjan}@grinnell.edu

;;; Procedure:
;;;   square
;;; Parameters:
;;;   val, a number
;;; Purpose:
;;;   Compute val*val
;;; Produces:
;;;   result, a number
;;; Preconditions:
;;;   val must be a number
;;; Postconditions:
;;;   result is the same "type" of number as val (e.g., if
;;;   val is an integer, so is result; if val is exact,
;;;   so is result).
;;; Citations:
;;;   Based on code created by John David Stone dated March 17, 2000
;;;   and contained in the Web page 
;;;   http://www.math.grin.edu/~stone/courses/scheme/procedure-definitions.xhtml
;;;   Changes to
;;;     Parameter names
;;;     Formatting
;;;     Comments
(define square
  (lambda (value)
    (* value value)))

Yes, that's a lot of documentation for very little code. However, it is better to err on the side of too much documentation than too little documentation. More importantly, as you start writing more procedures, their purpose and details will be much less obvious when you come back to them. Finally, when you carefully document procedures, you begin to think more carefully about what they really need to do and how you ensure that they do so for all cases.

Another Example: Grading Homework

The square computation is fairly simple. We can, of course, write more complex expressions. For example, consider the problem of generating an average numeric grade, given a six grades (on exams, quizzes, whatever). A pure average would simply add the six numbers together and divide by six.

(define compute-grade
  (lambda (grade1 grade2 grade3 grade4 grade5 grade6)
    (/ (+ grade1 grade2 grade3 grade4 grade5 grade6 )
       6)))

A more generous policy for dealing with such grades would be to drop the lowest grade and count the highest grade twice. We might express that policy as

(define compute-grade
  (lambda (grade1 grade2 grade3 grade4 grade5 grade6)
    (/ (+ grade1 grade2 grade3 grade4 grade5 grade6 
          (max grade1 grade2 grade3 grade4 grade5 grade6)
          (- (min grade1 grade2 grade3 grade4 grade5 grade6)))
        6)))

Of course, it would be nice to make this procedure work with a varying number of homework grades. We'll see one such strategy in a few days, when we begin to explore lists.

Documenting the compute-grade Procedure

We just described a variety of compute-grade procedures. Believe it or not, but we can write a common set of documentation for them:

;;; Procedure:
;;;   compute-grade
;;; Parameters:
;;;   grade1, a real number
;;;   grade2, a real number
;;;   grade3, a real number
;;;   grade4, a real number
;;;   grade5, a real number
;;;   grade6, a real number
;;; Purpose:
;;;   Compute a weighted average of the six grades, using the
;;;   top-secret course grading policy.
;;; Produces:
;;;   grade, a number
;;; Preconditions:
;;;   All grades must be non-negative.
;;; Postconditions:
;;;   grade is no smaller than the smallest of the six grades.
;;;   grade is no larger than the largest of the six grades.
;;;   grade is non-negative (implied by previous postconditions).

It turns out that writing this documentation helped us think a bit more about some particular issues that relate to the procedure. For example, we needed to specify something about the result that the client would find useful. At the same time, we wanted to keep the documentation general enough that we could use either policy. That made us decide that putting limits on the result was appropriate. Those limits are, however limiting. We cannot, for example, write a procedure that gives as student who does every assignment a bit of extra credit, since that might lead to a grade higher than any given so far.

We also had to specify that the procedure took numbers as inputs and produced a number. Without such consideration, we might have had an awkward moment in which someone called our procedure with letter grades, or expected it to return a letter grade.


Janet Davis (davisjan@cs.grinnell.edu)