name you know when you write the code. In fact, the previous two expressions will quite likely compile to exactly the same machine instructions.

The following function demonstrates a more apt use of FUNCALL. It accepts a function object as an argument and plots a simple ASCII-art histogram of the values returned by the argument function when it's invoked on the values from min to max, stepping by step.

(defun plot (fn min max step)

(loop for i from min to max by step do

(loop repeat (funcall fn i) do (format t '*'))

(format t '~%')))

The FUNCALL expression computes the value of the function for each value of i. The inner LOOP uses that computed value to determine how many times to print an asterisk to standard output.

Note that you don't use FUNCTION or #' to get the function value of fn; you want it to be interpreted as a variable because it's the variable's value that will be the function object. You can call plot with any function that takes a single numeric argument, such as the built-in function EXP that returns the value of e raised to the power of its argument.

CL-USER> (plot #'exp 0 4 1/2)

*

*

**

****

*******

************

********************

*********************************

******************************************************

NIL

FUNCALL, however, doesn't do you any good when the argument list is known only at runtime. For instance, to stick with the plot function for another moment, suppose you've obtained a list containing a function object, a minimum and maximum value, and a step value. In other words, the list contains the values you want to pass as arguments to plot. Suppose this list is in the variable plot-data. You could invoke plot on the values in that list like this:

(plot (first plot-data) (second plot-data) (third plot-data) (fourth plot-data))

This works fine, but it's pretty annoying to have to explicitly unpack the arguments just so you can pass them to plot.

That's where APPLY comes in. Like FUNCALL, the first argument to APPLY is a function object. But after the function object, instead of individual arguments, it expects a list. It then applies the function to the values in the list. This allows you to write the following instead:

(apply #'plot plot-data)

As a further convenience, APPLY can also accept 'loose' arguments as long as the last argument is a list. Thus, if plot-data contained just the min, max, and step values, you could still use APPLY like this to plot the EXP function over that range:

(apply #'plot #'exp plot-data)

APPLY doesn't care about whether the function being applied takes &optional, &rest, or &key arguments—the argument list produced by combining any loose arguments with the final list must be a legal argument list for the function with enough arguments for all the required parameters and only appropriate keyword parameters.

Anonymous Functions

Once you start writing, or even simply using, functions that accept other functions as arguments, you're bound to discover that sometimes it's annoying to have to define and name a whole separate function that's used in only one place, especially when you never call it by name.

When it seems like overkill to define a new function with DEFUN, you can create an 'anonymous' function using a LAMBDA expression. As discussed in Chapter 3, a LAMBDA expression looks like this:

(lambda (parameters) body)

One way to think of LAMBDA expressions is as a special kind of function name where the name itself directly describes what the function does. This explains why you can use a LAMBDA expression in the place of a function name with #'.

(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5

You can even use a LAMBDA expression as the 'name' of a function in a function call expression. If you wanted, you could write the previous FUNCALL expression more concisely.

((lambda (x y) (+ x y)) 2 3) ==> 5

But this is almost never done; it's merely worth noting that it's legal in order to emphasize that LAMBDA expressions can be used anywhere a normal function name can be.[66]

Anonymous functions can be useful when you need to pass a function as an argument to another function and the function you need to pass is simple enough to express inline. For instance, suppose you wanted to plot the function 2x. You could define the following function:

(defun double (x) (* 2 x))

which you could then pass to plot.

CL-USER> (plot #'double 0 10 1)

**

****

******

********

**********

************

**************

****************

******************

********************

NIL

But it's easier, and arguably clearer, to write this:

CL-USER> (plot #'(lambda (x) (* 2 x)) 0 10 1)

**

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату