• Aucun résultat trouvé

Example: Utilities

Dans le document ANSI Common Lisp (Page 121-124)

Specialized Data Structures

Section 2.13 mentioned that the first argument to do had to be a list of specifications for variables, each possibly of the form

6.4 Example: Utilities

Section 2.6 mentioned that Lisp consists mostly of Lisp functions, just like the ones you can define yourself. This is a useful feature to have in a programming language: you don't have to modify your ideas to suit the language, because you can modify the language to suit your ideas. If you find yourself wishing that Common Lisp included a certain function, you can write it yourself, and it will be just as much a part of the language as + or eql.

Experienced Lisp programmers work bottom-up as well as top-down.

While they're writing their program down toward the language, they also build the language up toward their program. This way, language and program meet sooner, and more neatly.

Operators written to augment Lisp are called utilities. As you write more Lisp programs, you will find that you develop a collection of them, and that many of the utilities you write during one project will turn out to be useful in the next one.

Professional programmers often find that the program they're working on now has a great deal in common with some program they wrote in the past. It is this feeling that makes the idea of software reuse so attractive.

Somehow reuse has become associated with object-oriented programming.

But software does not have to be object-oriented to be reusable—this is obvious when we look at programming languages (that is, compilers), which are the most reusable software of all.

The way to get reusable software is to write programs bottom-up, and programs don't have to be object-oriented to be written bottom-up. In fact, the functional style seems even better adapted for writing reusable software.

Consider s o r t . You are unlikely ever to have to write your own sort routines in Common Lisp; s o r t is so fast and so general that it would not be worth the trouble. That's reusable software.

You can do the same thing in your own programs by writing utilities.

Figure 6.1 contains a selection of them. The first two, s i n g l e ? and appendl, are included to show that even very short utilities can be useful. The former returns true when its argument is a list of one element,

> ( s i n g l e ? ' ( a ) ) T

and the latter is like cons, but adds an element to the end of the list instead of the front:

> (appendl ' ( a b c) >d) (A B C D)

The next utility, map-int, takes a function and an integer n, and returns a list of the results of calling the function on the integers from 0 to n-Y

6.4 EXAMPLE: UTILITIES 105

(defun single? (1st)

(and (consp 1st) (null (cdr 1st)))) (defun appendl (1st obj)

(append 1st (list obj))) (defun map-int (fn n)

(let ((ace nil)) (dotimes (i n)

(push (funcall fn i) ace)) (nreverse ace)))

(defun filter (fn 1st) (let ((ace nil))

(dolist (x 1st)

(let ((val (funcall fn x))) (if val (push val ace)))) (nreverse ace)))

(defun most (fn 1st) (if (null 1st)

(values nil nil)

(let* ((wins (car 1st))

(max (funcall fn wins))) (dolist (obj (cdr 1st))

(let ((score (funcall fn obj))) (when (> score max)

(setf wins obj

max score)))) (values wins max))))

Figure 6.1: Utility functions.

This turns out to be especially useful when one is testing code. (One of the advantages of Lisp's interactive environment is that it's easy to write programs to test your programs.) If we just wanted a list of the numbers from 0 to 9, we could say:

> (map-int # ' i d e n t i t y 10) (0 1 2 3 4 5 6 7 8 9)

And if we wanted a list of 10 random numbers between 0 and 99 (inclusive), we could ignore the parameter and just say:

> (map-int #'(lambda (x) (random 100)) 10)

(85 40 73 64 28 21 40 67 5 32)

The definition of map-int illustrates one of the standard Lisp idioms for building a list. We create an accumulator ace, initially n i l , and push suc-cessive objects onto it. When we're finished, we reverse the accumulator.1

We see the same idiom in f i l t e r . This function takes a function and a list, and returns all the non-nil values returned by the function as it is applied to the elements of the list:

> (filter #>(lambda (x)

(and (evenp x) (+ x 10))) ' ( 1 2 3 4 5 6 7))

(12 14 16)

Another way to think of f i l t e r is as a generalized version of remove-if.

The last function in Figure 6.1, most, returns the element of a list with the highest score, according to some scoring function. It returns two values, the winning element, and its score:

> (most # ' l e n g t h ' ( ( a b) (a b c) ( a ) ) ) (A B C)

3

If there is a tie, the element occurring first is returned.

Notice that the last three functions in Figure 6.1 all take functions as arguments. Lisp makes it convenient to pass functions as arguments, and that's one of the reasons it is so well suited to bottom-up programming.0 A successful utility must be general, and it's easier to abstract out the general when you can pass the specific as a functional argument.

The functions given in this section were general-purpose utilities. They could be used in almost any kind of program. But you can write utilities for specific classes of programs as well. Indeed, as we'll see when we get to macros, you can write your own specialized languages on top of Lisp, if you want to. If you are trying to write reusable software, this would seem the surest way to do it.

'In this context, nreverse (described on page 222) does the same thing as reverse, but is more efficient.

6.5 CLOSURES 107

6.5 Closures

A function can be returned as the value of an expression just like any other kind of object. Here is a function that takes one argument, and returns a function to combine arguments of that type:

(defun combiner (x) (typecase x

(number #'+)

(list #'append) (t #'list)))

On top of this we can build a general combination function (defun combine (ferest args)

(apply (combiner (car args)) args))

which takes arguments of any type and combines them in a way appropriate to their type. (To simplify the example, we assume that the arguments will all be of the same type.)

> (combine 2 3) 5

> (combine '(a b) '(c d)) (A B C D)

Section 2.10 mentioned that lexical variables are only valid within the

Dans le document ANSI Common Lisp (Page 121-124)