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
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.
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:
number? tests whether its argument is a number.symbol? tests whether its argument is a symbol.string? tests whether its argument is a string.procedure? tests whether its argument is a procedure.list? tests whether its argument is a list.null? tests whether its argument is the empty list.boolean? tests whether its argument is a Boolean value.eq? tests whether its two arguments are identical, in
the very narrow sense of occupying the same storage location in the
computer's memory. In practice, this is useful information only if at
least one argument is known to be a symbol or a Boolean value.eqv? tests whether its two arguments ``should normally be
regarded as the same object'' (as the language standard declares). Note,
however, that two lists can have the same elements without being ``regarded
as the same object.'' Also note that in Scheme's view the number 5, which
is ``exact,'' is not necessarily the same object as the number 5.0, which
might be an approximation.equal? tests whether its two arguments are the same or,
in the case of lists, whether they have the same contents.= tests whether its arguments, which must all be numbers, are
numerically equal; 5 and 5.0 are numerically equal for this purpose.< tests whether its arguments, which must all be real numbers,
are in strictly ascending numerical order.> tests whether its arguments, which must all be real numbers,
are in strictly descending numerical order.<= tests whether its arguments, which must all be real
numbers, are in ascending numerical order, allowing equality.>= tests whether its arguments, which must all be real
numbers, are in descending numerical order, allowing equality.even? tests whether its argument, which must be an integer, is
even.odd? tests whether its argument, which must be an integer, is
odd.zero? tests whether its argument, which must be a number, is
equal to zero.positive? tests whether its argument, which must be a real
number, is positive.negative? tests whether its argument, which must be a real
number, is negative.
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.