Procedure definitions

Course links

A programmer can have Scheme construct a new procedure, not equivalent to any of the built-in ones, by writing a lambda-expression. For instance, Scheme recognizes the expression

(lambda (root)
  (* root root))

as a description of a procedure that, when called, squares its argument. Like any other value, this procedure can be given a name by means of a definition:

;;; square: multiply a given number by itself

;;; John David Stone
;;; Department of Mathematics and Computer Science
;;; Grinnell College
;;; stone@cs.grinnell.edu

;;; created February 13, 1999
;;; last revised March 17, 2000

;;; Given:
;;;   ROOT, an exact number.

;;; Result:
;;;   SQUARED, an exact number.

;;; Preconditions:
;;;   None.

;;; Postcondition:
;;;   SQUARED is the product of ROOT and ROOT.

(define square
  (lambda (root)
    (* root root)))

Such a procedure can be called, just as if it were a built-in procedure:

> (square 12)
144

Comments on programs and procedure definitions

When defining a procedure, it's a good idea to include several things in one's introductory comments: the name and purpose of the procedure, the number and nature of each argument that it takes, the nature of the result it returns, the additional conditions that must be met before a caller to the procedure can expect it to work correctly, and the conditions that the procedure promises to satisfy when it has finished its work.

If the procedure is a complicated one, it's also a good idea to describe in your opening comment how the procedure does its work. But you should avoid giving a play-by-play description of each step in the computation: The Scheme code itself is the low-level description of its own operation. The opening comment should supplement the code, not duplicate it.

Another good habit to form is including author information and creation and revision dates in every Scheme program that you write. It's sufficient to present this information once at the top of each file that you save, rather than at the beginning of each procedure definition. A program-level opening comment often includes a general discussion of the program's nature and purpose and an overview of the groups of procedure definitions that make up the bulk of a large Scheme program.

Booleans and predicates

A Boolean value is a datum that reflects the outcome of a single yes-or-no test. For instance, if one were to ask Scheme to compute whether the empty list has five elements, it would be able to determine that it does not, and it would signal this result by displaying the Boolean value for ``no'' or ``false,'' which is #f. There is only one other Boolean value, the one meaning ``yes'' or ``true,'' which is #t. (These are called ``Boolean values'' in honor of the logician George Boole, who was the first to develop a satisfactory formal theory of them.)

A predicate is a procedure that always returns a Boolean value. A procedure call in which the procedure is a predicate performs some yes-or-no test on its arguments. For instance, the predicate number? -- the question mark is part of the name of the procedure -- takes one argument and returns #t if that argument is a number, #f if it does not. Similarly, the predicate even? takes one argument, which must be an integer, and returns #t if the integer is even and #f if it is odd. The names of most Scheme predicates end with question marks, and I recommend this useful convention even though it is not required by the rules of the programming language.

Here is a selection of ``primitive'' (that is, built-in) Scheme predicates:

Another useful Boolean procedure is not, which takes one argument and returns #t if the argument is #f and #f if the argument is anything else. For example, one can test whether the square root of 100 is unequal to the absolute value of -12 by giving the command

(not (= (sqrt 100) (abs -12)))

If Scheme says that the value of this expression is #t, then the two numbers are indeed unequal.

I am indebted to Professor Ben Gum for his contributions to the development of this reading.