A higher-order procedure is one that can take procedures as arguments and/or return them as values. We can use that to write general procedures that do a basic kind of thing, and take arguments that specialize their behavior. The arguments are themselves procedures, which will do specialized things withing the general pattern that the general procedure implements.
Here's a simple example.
Scheme provides a procedure
can write textual representation of a data object on the screen, much like
the way the read-eval-print loop displays results of expressions you type
in. (This is a very handy procedure for debugging, as well as for programs
that interact with users.)
Suppose, though, that you want to display a list of objects, not just
one. You want a routine
list-display to iterate over a list, and
display each item in it. The obvious way to write it is to just call
display from inside your
display can display a list of items, but it puts
parentheses around the items in the list. Let's suppose we don't want
those parentheses around the
displayed items. Writing our
list-display will give us the freedom to make it do whatever
we want it to, rather than what
display does automatically for
Here's a version like that:
Scheme>(define (list-display lis) (if (null? lis) #f (begin (display (car lis)) (list-display (cdr lis)))))
I've written this procedure recursively, because it's easy to use
recursion over lists--usually it's easier than using an iteration
construct. This procedure checks to see if the list it got
was empty, and if so, it returns #f. (That's a reasonable value
to return from a procedure that's used for effect, rather than
for value.) Otherwise, it displays the first item, and then calls itself
recursively to display the rest of the list. I used a
to sequence the displaying and the recursive call.
It would be cleaner to use
cond, so here's an equivalent
Scheme>(define (list-display lis) (cond ((null? lis) #f) (else (display (car lis)) (list-display (cdr lis)))))
Notice that this is a two-branch conditional, but we use
if because a
cond branch can be a sequence.
(We need a sequence because we want to use display to create a side-effect,
i.e., writing to the user's screen, as well as calling list-display
recursively to do the rest of the work.)
Now try it out:
Scheme>(list-display '(1 2 3)) 123#f
What happened here is that it displayed each item in the list as it
was evaluated, and then Scheme printed out the return value,
This works, but the procedure is not very general. Iterating over lists is very common, so it would be nice to have a more general procedure that iterates over lists, and applies whatever procedure you want.
We can modify our procedure to do this. Instead of taking just a list argument, it can take an argument that's a procedure, and apply that procedure to each element of the list.
We'll call our procedure
list-each, because it iterates
over a list and does whatever you want to each element.
Scheme>(define (list-each proc lis) (cond ((null? lis) #f) (else (proc (car lis)) (list-each proc (cdr lis)))))
The only change we made was to add an argument
proc, to accept
(a pointer to) a procedure, and to change the call to
into a call to
Remember that procedure names are really just names of variables that
hold pointers to procedures, so this works---
(proc (car lis))
is just a combination whose first expression is
looks up the value of the local variable
Now we can call this general procedure with the argument
to tell it to
display each thing in the list.
Scheme>(list-each display '(1 2 3)) 123#f
But maybe this isn't what we want. We might want to print each item,
and then a newline (go to the next line), to spread things out vertically.
We could write a procedure
display-with-newline to do that,
but it's easier just to use a
lambda expression to create the
procedure we need.
Scheme>(list-each (lambda (x) (display x) (newline)) '(1 2 3)) 1 2 3 #void
lambda expression creates a one-argument procedure that will
display its argument and then call
newline. We pass the
procedure that results from this
lambda directly to
without ever giving it a name, or saving a pointer to it anywhere.
(After list-each is through with it, the procedure will become garbage
and its space can be reclaimed by the garbage collector.)
(Scheme has a standard procedure similar to our
but more general, called
================================================================== This is the end of Hunk L At this point, you should go back to the previous chapter and read Hunk M before returning here and continuing this tutorial. ==================================================================
(Go BACK to read Hunk M, which starts at section
lambda and Lexical Scope (Hunk M).)