Another way to get repetitive operations
There's yet another way to get repetition out of our
procedures, and that falls under the header of "applicative
programming" as opposed to "recursive programming". To
demonstrate how applicative programming works, though, we'll
start with a recursive explanation of applicative
programming.
When using a programming language whose basic data structure
is the list, it's not surprising that one of the things we
want to do most often is perform the same procedure on all
the elements of a list. We already know how to do that
recursively. Say for example, that we want to take a list of
numbers as input and return a list of corresponding square
roots. That procedure might look like this (do you recognize
the recursion template?):
(defun lotsa-sqrts (input-list)
(cond ((null input-list) nil)
(T (cons (sqrt (first input-list))
(lotsa-sqrts (rest input-list))))))
and it would work like this:
? (lotsa-sqrts '(1 4 9 16 25))
(1 2 3 4 5)
?
Now say instead of sqrt I want to use "double" to double every element
of my list, as in
(defun double (x)
(* 2 x))
? (lotsa-doubles '(1 2 3 4))
(2 4 6 8)
?
It's easy to imagine how to write that. Just write a new function where
every occurrence of "sqrt" is replaced by "double".
_OR_, instead of writing anything new, it turns out we can just say
(mapcar #'double '(1 2 3 4)) => (2 4 6 8)
(mapcar #'sqrt '(1 4 9 16)) => (1 2 3 4)
Huh?
Functions as data
I've jumped ahead of myself, introducing two new functions: mapcar and
hash-quote #' in order to show you the result we're aiming at. Let's
back up the truck (beep beep!) and see what's going on.
In order to make this happen, there are some things you need
to know first. Remember way back when we were touting all
the things that made LISP so nice? One of those features was
the lack of any procedure/data distinction. Here's one place
where that feature comes in handy.
Since a procedure can be treated as a piece of data, we can
decompose procedures into component parts. That means we can
separate a function body from its name. (How did that body
get that name? The "defun" function did it, remember?) In
other words, it's possible (and in many cases desirable, as
we're about to see) to use a nameless, anonymous function
definition in our computations. LISP of course has a
predefined function for separating function bodies from their
names, and it is named, ironically, "function". Here's how
it works:
Let's say you've defined a new function of your own, which in
this case takes a number as an argument and returns its
double. When you use "defun" to create that function:
(defun double (x)
(* 2 x))
you've told LISP to bind the name "double" to everything that
follows the name "double" in your definition. That association
is then stored in the symbol table. Later, when you call the
"double" function, LISP looks up "double" in the symbol table and
retrieves the function body associated with it, then uses
that function body to figure out what to do. If we want to
tell LISP at some point to explicitly go to the symbol table,
look up the "double" function, retrieve the function body and
return it without doing anything else with it, then we can
just say:
> (function double)
Now, if we're doing this in some form of Common LISP that used
to be available here, we'd see this:
> (function double)
(LAMBDA (X) (* 2 X))
>
Actually, we might see more junk in there, but it would be
implementation-dependent detail. What you see just above is
the function body in its "pure" form.
What you're looking at is LISP's basic anonymous function
form, called the "lambda function". The "(x)" part is
the argument list, and the rest is just the function body.
Thus, a lambda function is just a function body that's not
bound to a function name.
That "function" function gets used a lot, it turns out, so
there's an abbreviation. Instead of typing
? (function double)
we could type
? #'double
and get the same result. That abbreviation is pronounced
"hash-quote".
What can you do with a lambda function?
Back to the original problem: how do we create a generic
function like "lotsa-sqrts" that isn't restricted to just
computing square roots? How do we get it to use any function
we pass it as an argument? How do we turn "lotsa-sqrts" into
"lotsa-anything"?
Let's borrow the "lotsa-sqrts" and make a few necessary
changes and see where that gets us:
(defun lotsa-anything (func-body input-list) ; two args now
(cond ((null input-list) nil) ; nothing new
(T (cons (*magic* func-body (first input-list)) ; eh?
(lotsa-anything func-body (rest input-list))))))
We know we needed to pass a function body as an argument, so
we added that argument. Everything else is pretty much the
same, except we don't know how to make the function body
passed as "func-body" do anything to the argument passed as
"input-list". All we have there is the function body, and
since LISP lets us separate function bodies from their names,
there must be some way of using a nameless function body,
right? I hope you said yes, because it's true. And that's
what we need to put in place of *magic* up above in "lotsa-
anything". (No, *magic* is not literally a predefined LISP
function. It'd be way cool if it were.)
LISP actually provides two basic functions for applying
anonymous function bodies to arguments. The first is called
(you guessed it) "apply". The "apply" function takes two
arguments, a function body and a list whose elements are the
arguments to be passed to the function body. The function
body is then applied to the that list of arguments (i.e.,
variable bindings are made, the function is then executed,
the result is returned...just like in "normal" function
evaluation). The "apply" function is the function to choose
when you don't know how many arguments to expect (i.e., the
anonymous function can take on an arbitrary number of
arguments). For example, we can say:
? (apply #'+ '(2 4 6 8))
20
and all is ok. Apply expects an arbitrarily long list of
arguments, and "+" can accept any number of arguments. But
if we say:
? (apply #'+ 2 4 6 8)
we get an error:
> Error: Can't construct argument list from 8.
> While executing: APPLY
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >
On the other hand, we can do this:
? (apply #'+ 2 4 '(6 8))
20
?
The way that "apply" is usually used is with two arguments: a function
body and a list of arguments for that function body. But, as a
"convenience" (as one book puts it), "apply" can take any number
of arguments so long as the last one is a list. So while
(apply #'+ 2 4 6 8) won't work, (apply #'+ 2 4 (6 8)) will work.
Personally, I never use "apply" in this way, and I almost always
forget that I can.
What if you do know in advance how many arguments your function
expects, like in the case of the "sqrt" function, which always
takes only one argument? In this case, we can use "funcall"
instead of "apply". The "funcall" function takes a function
body as its first argument, and the remaining arguments are
exactly those arguments needed by the function body passed as
the first argument. Thus, we can say:
? (funcall #'sqrt 25)
5
?
but we can't say:
? (funcall #'sqrt 25 36)
> Error: Too many arguments.
> While executing: SQRT
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >
because we'd be trying to pass too many arguments to "sqrt",
and we can't say:
? (funcall #'sqrt '(25 36))
> Error: value (25 36) is not of the expected type NUMBER.
> While executing: ZEROP
> Type Command-. to abort.
See the Restarts... menu item for further choices.
1 >
because "sqrt" expects a number, not a list. That was easy,
wasn't it?
So now, which of those two functions, "apply" and "funcall",
would be the right one to put in place of *magic* in our
"lotsa-anything" function? We'd replace *magic* with
"funcall":
(defun lotsa-anything (func-body input-list)
(cond ((null input-list) nil)
(T (cons (funcall func-body (first input-list))
(lotsa-anything
func-body (rest input-list))))))
The net result is a function which maps another function onto
the elements of a list, performs that operation on each of
those elements, and returns a list of the results.
Because "lotsa-anything" sort of scoots along, mapping the
passed function body onto succeeding "first"s or "car"s of
the ever-shrinking "input-list", this function resembles a
function called "mapcar". The "mapcar" function is already
predefined in LISP, and works sort of like what we've defined
here: it applies the given function to successive "first"s or
"car"s of the list, collects the individual results into a list,
and returns that list. Despite the similarities, "mapcar" isn't
the same as "lotsa-anything"---"mapcar" is much more powerful.
So while building the "lotsa-anything" function gives us an idea
as to what the "mapcar" and its associated mapping functions do,
you should take the time to look up "mapcar" and the others in the
Steele book and/or the Graham book and become familiar with
what's really going on there.
Here's how I can substitute the elegant, pre-defined "mapcar" function
for my crude "lotsa-anything" slop and get the same result:
? (mapcar #'sqrt '(1 4 9 16 25))
(1 2 3 4 5)
?
One thing that "mapcar" can do that "lotsa-anything" can't is
take functions that expect more than one argument. If, for
example, I wanted to "cons" a bunch of elements of one list
into their respective elements of another list, I'd just do
this:
? (mapcar #'cons '(a b c) '(1 2 3))
((A . 1) (B . 2) (C . 3))
?
(Dotted pairs...yuk! Anyway, we move on...)
There's a corresponding function called "maplist" which maps the
given function onto successive "rest"s or "cdr"s of the list,
collects the results into a list, and returns that list. As I
mentioned in class, "maplist" is of limited usefulness, but here's
a semi-pointless example that we generated as a result of an in-class
discussion a couple of quarters ago...
Let's say I wanted to compute the factorials (of course!) of a
sequence of numbers from N to 1 which I conveniently have
already stored in a list like this: (5 4 3 2 1).
I could write two not-so-obvious functions like this:
(defun list-factorial (num-list)
(maplist #'list-multiply num-list))
(defun list-multiply (num-list)
(apply #'* num-list))
and then do this:
? (list-factorial '(5 4 3 2 1))
(120 24 6 2 1)
?
Ooohhh!! Neat! OK, it's not a real practical example, but it
works.
It's these predefined mapping functions like "mapcar" that
provide us with a second means of getting repetitive
operations without the traditional looping structure. The
mapping functions are very efficient in applying a function
in sequence to different groupings of an argument list.
Again, this style of programming is called "applicative
programming" to distinguish it from "recursion" or
"iteration".
Note:
You may get the impression from the discussion above that you
can only use apply and funcall with a named function whose body
is then extracted via #' (i.e., the function function). Not
true at all. If the sqr function is defined as follows:
? (defun sqr (x) (* x x))
SQR
then the following are equivalent:
? (apply #'sqr '(2))
4
? (funcall #'sqr 2)
4
? (apply #'(lambda (x) (* x x)) '(2))
4
? (funcall #'(lambda (x) (* x x)) 2)
4
? (apply (function (lambda (x) (* x x))) '(2))
4
? (funcall (function (lambda (x) (* x x))) 2)
4
So note that I could construct a function which builds a list
that just happens to be a lambda function, plunk that inside
a "function" function, and put that inside either "apply" or
"funcall", and thereby evaluate a lambda function that was
constructed in real time by another function. Piece o' cake.
Now we turn and wave goodbye to that grunt-level implementation
stuff and move on to questions about knowledge and how we
represent it.
Lecture notes by Kurt Eiselt, 1998.
Minor changes / additions by Brian McNamara, 1998.
Last updated on
Tue Jul 14 05:29:16 EDT 1998
by Brian McNamara