• Aucun résultat trouvé

Looking Forward

Dans le document ANSI Common Lisp (Page 44-51)

Welcome to Lisp

SUM-GREATER

2.16 Looking Forward

In this chapter we have barely scratched the surface of Lisp. And yet a portrait of a very unusual language is beginning to emerge. To start with, the language has a single syntax to express all program structure. This syntax is based on the list, which is a kind of Lisp object. Functions, which are Lisp objects in their own right, can be expressed as lists. And Lisp is itself a Lisp program, made almost entirely of Lisp functions no different from the ones you can define yourself.

Don't worry if the relations between all these ideas are not entirely clear.

Lisp introduces so many novel concepts that it takes some time to get used to all the new things you can do with it. One thing should be clear at least:

there are some startlingly elegant ideas here.

Richard Gabriel once half-jokingly described C as a language for writing Unix.0 We could likewise describe Lisp as a language for writing Lisp. But this is a different kind of statement. A language that can be written in itself is fundamentally different from a language good for writing some particular class of applications. It opens up a new way of programming: as well as writing your program in the language, you can improve the language to suit your program. If you want to understand the essence of Lisp programming, this idea is a good place to begin.

Summary

1. Lisp is an interactive language. If you type an expression into the toplevel, Lisp will display its value.

2. Lisp programs consist of expressions. An expression can be an atom, or a list of an operator followed by zero or more arguments. Prefix syntax means that operators can take any number of arguments.

3. The evaluation rule for Common Lisp function calls: evaluate the arguments left to right, and pass them to the function denoted by the operator. The quote operator has its own evaluation rule, which is to return the argument unchanged.

4. Along with the usual data types, Lisp has symbols and lists. Because Lisp programs are expressed as lists, it's easy to write programs that write programs.

5. The three basic list functions are cons, which builds a list; car, which returns the first element; and cdr, which returns everything after the first element.

6. In Common Lisp, t represents true and n i l represents false. In a logical context, anything except n i l counts as true. The basic conditional is if. The and and or operators resemble conditionals.

7. Lisp consists mainly of functions. You can define new ones with defun.

8. A function that calls itself is recursive. A recursive function should be considered as a process rather than a machine.

9. Parentheses are not an issue, because programmers read and write Lisp by indentation.

10. The basic I/O functions are read, which includes a complete Lisp parser, and format, which generates output based on templates.

EXERCISES 29 11. You can create new local variables with l e t , and global variables with

defparameter.

12. The assignment operator is s e t f . Its first argument can be an expres-sion.

13. Functional programming, which means avoiding side-effects, is the dominant paradigm in Lisp.

14. The basic iteration operator is do.

15. Functions are regular Lisp objects. They can be passed as arguments, and denoted by lambda expressions.

16. In Lisp, values have types, not variables.

Exercises

1. Describe what happens when the following expressions are evaluated:

(a) (+ (- 5 1) (+ 3 7)) (b) ( l i s t 1 ( + 2 3))

(c) ( i f ( l i s t p 1) (+ 1 2 ) ( + 3 4 ) ) (d) ( l i s t (and ( l i s t p 3) t ) (+ 1 2))

2. Give three distinct cons expressions that return (a b c ) .

3. Using car and cdr, define a function to return the fourth element of a list.

4. Define a function that takes two arguments and returns the greater of the two.

5. What do these functions do?

(a) (defun enigma (x) (and (not (null x ) )

(or (null (car x ) ) (enigma (cdr x))))) (b) (defun mystery (x y )

(if (null y ) nil

(if (eql (car y ) x ) 0

(let ((z (mystery x (cdr y ) ) ) ) (and z (+ z 1))))))

6. What could occur in place of the x in each of the following exchanges?

(a) > (car (x (cdr ' ( a (b c) d ) ) ) ) B

(b) > (x 13 ( / 1 0)) 13

(c) > (x # > l i s t 1 n i l ) (1)

7. Using only operators introduced in this chapter, define a function that takes a list as an argument and returns true if one of its elements is a list.

8. Give iterative and recursive definitions of a function that (a) takes a positive integer and prints that many dots.

(b) takes a list and returns the number of times the symbol a occurs ink.

9. A friend is trying to write a function that returns the sum of all the non-nil elements in a list. He has written two versions of this function, and neither of them work. Explain what's wrong with each, and give a correct version:

(a) (defun summit ( 1 s t ) (remove nil 1st) (apply #' + 1st)) (b) (defun summit (1st)

(let ((x (car 1st))) (if (null x )

(summit (cdr 1st))

(+ x (summit (cdr 1st))))))

3

Lists

Lists are one of the fundamental data structures in Lisp. In the earliest dialects they were the only data structure: the name "Lisp" originally stood for "LISt Processor." But Lisp has long since outgrown this acronym. Common Lisp is a general-purpose programming language with a wide variety of data structures.

The development of Lisp programs often echoes the development of Lisp itself. In the initial version of a Lisp program, you may use a lot of lists.

Then in later versions you may switch to faster, specialized data structures.

This chapter describes the many things you can do with lists, and uses them to illustrate some general Lisp concepts.

3.1 Conses

Section 2.4 introduced cons, car, and cdr, the primitive list-manipulation functions. What cons really does is combine two objects into a two-part object called a cons. Conceptually, a cons is a pair of pointers; the first one is the car and the second is the cdr.

Conses provide a convenient representation for pairs of any type. The two halves of a cons can point to any kind of object, including conses. It is by taking advantage of the latter possibility that we use conses to build lists.

One does not tend to think of lists as pairs, but they can be defined that way. Any nonempty list can be considered as a pair of the first element and the rest of the list. Lisp lists are the embodiment of this idea. We use one half of the cons to point to the first element of the list, and the other to point to the rest of the list (which is either another cons or n i l ) . The convention

31

a

Figure 3.1: A one-element list.

nil

a b c Figure 3.2: A list of three elements.

in Lisp has always been to use the car for the first element and the cdr for the rest of the list. So now car is synonymous with the first element of a list, and cdr with the rest. Lists are not a distinct kind of object, but conses linked together in this way.

When we cons something onto n i l ,

> ( s e t f x (cons ' a n i l ) ) (A)

the resulting list consists of a single cons, as shown in Figure 3.1. This way of representing conses is called box notation, because each cons is shown as a box, with pointers for the car and cdr. When we call car and cdr, we get back what those pointers point to:

> ( c a r x) A

> (cdr x) NIL

When we build a list with multiple elements, we get a chain of conses:

> ( s e t f y ( l i s t ' a ' b ' c ) ) (A B C)

The resulting structure is shown in Figure 3.2. Now when we ask for the cdr of this list, it is itself a list of two elements:

3.2 CONSES 33

1 i

1 1 1 1 1 1

1 1

*\

1 1 1 * 1 1

I I I 1

J l 1

1

n j l c

1 1

b c Figure 3.3: A nested list.

1 1.1

f

J

> (cdr y) (B C)

In a list of several elements, the car pointers get you the elements, and the cdr pointers get you the rest of the list.

A list can have any kind of object as an element, including another list:

> ( s e t f z ( l i s t ' a ( l i s t ' b > c) >d)) (A (B C) D)

When this happens, the underlying structure is as shown in Figure 3.3; the car pointer of the second cons in the chain also points to a list:

> (car (cdr z ) ) (B C)

The last two lists we made both have three elements; it just happens that the second element of z is also a list. Such a list is called a nested list, while a list like y that doesn't contain other lists as elements is called aflat list.

The function consp returns true if its argument is a cons. So l i s t p could be defined:

(defun our-listp (x) (or (null x) (consp x)))

Since everything that is not a cons is an atom, the predicate atom could be defined:

(defun our-atom (x) (not (consp x ) ) ) Note that n i l is both an atom and a list.

3.2 Equality

Each time you call cons, Lisp allocates a new piece of memory with room for two pointers. So if we call cons twice with the same arguments, we get back two values that look the same, but are in fact distinct objects:

> ( e q l (cons ' a n i l ) (cons ' a n i l ) ) NIL

It would be convenient if we could also ask whether two lists had the same elements. Common Lisp provides another equality predicate for this purpose:

equal. While e q l1 returns true only if its arguments are the same object,

> (setf x (cons 'a n i l ) ) (A)

> ( e q l x x) T

equal, essentially, returns true if its arguments would print the same:

> (equal x (cons 'a. n i l ) ) T

This predicate works for other kinds of structures besides lists, but a version for lists alone might be defined:

(defun our-equal (x y) (or (eql x y)

(and (consp x) (consp y)

(our-equal (car x) (car y)) (our-equal (cdr x) (cdr y)))))

As this definition suggests, if some x and y are eql, they are also equal.

Dans le document ANSI Common Lisp (Page 44-51)