Common LISP Hints Geoffrey J. Gordon <ggordon@cs.cmu.edu> Friday, February 5, 1993 Modified by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> Note: This tutorial introduction to Common Lisp was written for the CMU environment, so some of the details of running lisp toward the end may differ from site to site. Further Information The best LISP textbook I know of is Guy L. Steele Jr. _Common LISP: the Language_. Digital Press. 1984. The first edition is easier to read; the second describes a more recent standard. (The differences between the two standards shouldn't affect casual programmers.) A book by Dave Touretsky has also been recommended to me, although I haven't read it, so I can't say anything about it. Symbols A symbol is just a string of characters. There are restrictions on what you can include in a symbol and what the first character can be, but as long as you stick to letters, digits, and hyphens, you'll be safe. (Except that if you use only digits and possibly an initial hyphen, LISP will think you typed an integer rather than a symbol.) Some examples of symbols: a b c1 foo bar baaz-quux-garply Some things you can do with symbols follow. (Things after a ">" prompt are what you type to the LISP interpreter, while other things are what the LISP interpreter prints back to you. The ";" is LISP's comment character: everything from a ";" to the end of line is ignored.) > (setq a 5) ;store a number as the value of a symbol 5 > a ;take the value of a symbol 5 > (let ((a 6)) a) ;bind the value of a symbol temporarily to 6 6 > a ;the value returns to 5 once the let is finished 5 > (+ a 6) ;use the value of a symbol as an argument to a function 11 > b ;try to take the value of a symbol which has no value Error: Attempt to take the value of the unbound symbol B There are two special symbols, t and nil. The value of t is defined always to be t, and the value of nil is defined always to be nil. LISP uses t and nil to represent true and false. An example of this use is in the if statement, described more fully later: > (if t 5 6) 5 > (if nil 5 6) 6 > (if 4 5 6) 5 The last example is odd but correct: nil means false, and anything else means true. (Unless we have a reason to do otherwise, we use t to mean true, just for the sake of clarity.) Symbols like t and nil are called self-evaluating symbols, because they evaluate to themselves. There is a whole class of self-evaluating symbols called keywords; any symbol whose name starts with a colon is a keyword. (See below for some uses for keywords.) Some examples: > :this-is-a-keyword :THIS-IS-A-KEYWORD > :so-is-this :SO-IS-THIS > :me-too :ME-TOO Numbers An integer is a string of digits optionally preceded by + or -. A real number looks like an integer, except that it has a decimal point and optionally can be written in scientific notation. A rational looks like two integers with a / between them. LISP supports complex numbers, which are written #c(r i) (where r is the real part and i is the imaginary part). A number is any of the above. Here are some numbers: 5 17 -34 +6 3.1415 1.722e-15 #c(1.722e-15 0.75) The standard arithmetic functions are all available: +, -, *, /, floor, ceiling, mod, sin, cos, tan, sqrt, exp, expt, and so forth. All of them accept any kind of number as an argument. +, -, *, and / return a number according to type contagion: an integer plus a rational is a rational, a rational plus a real is a real, and a real plus a complex is a complex. Here are some examples: > (+ 3 3/4) ;type contagion 15/4 > (exp 1) ;e 2.7182817 > (exp 3) ;e*e*e 20.085537 > (expt 3 4.2) ;exponent with a base other than e 100.90418 > (+ 5 6 7 (* 8 9 10)) ;the fns +-*/ all accept multiple arguments There is no limit to the absolute value of an integer except the memory size of your computer. Be warned that computations with bignums (as large integers are called) can be slow. (So can computations with rationals, especially compared to the corresponding computations with small integers or floats.) Conses A cons is just a two-field record. The fields are called "car" and "cdr", for historical reasons. (On the first machine where LISP was implemented, there were two instructions CAR and CDR which stood for "contents of address register" and "contents of decrement register". Conses were implemented using these two registers.) Conses are easy to use: > (cons 4 5) ;Allocate a cons. Set the car to 4 and the cdr to 5. (4 . 5) > (cons (cons 4 5) 6) ((4 . 5) . 6) > (car (cons 4 5)) 4 > (cdr (cons 4 5)) 5 Lists You can build many structures out of conses. Perhaps the simplest is a linked list: the car of each cons points to one of the elements of the list, and the cdr points either to another cons or to nil. You can create such a linked list with the list fuction: > (list 4 5 6) (4 5 6) Notice that LISP prints linked lists a special way: it omits some of the periods and parentheses. The rule is: if the cdr of a cons is nil, LISP doesn't bother to print the period or the nil; and if the cdr of cons A is cons B, then LISP doesn't bother to print the period for cons A or the parentheses for cons B. So: > (cons 4 nil) (4) > (cons 4 (cons 5 6)) (4 5 . 6) > (cons 4 (cons 5 (cons 6 nil))) (4 5 6) The last example is exactly equivalent to the call (list 4 5 6). Note that nil now means the list with no elements: the cdr of (a b), a list with 2 elements, is (b), a list with 1 element; and the cdr of (b), a list with 1 element, is nil, which therefore must be a list with no elements. The car and cdr of nil are defined to be nil. If you store your list in a variable, you can make it act like a stack: > (setq a nil) NIL > (push 4 a) (4) > (push 5 a) (5 4) > (pop a) 5 > a (4) > (pop a) 4 > (pop a) NIL > a NIL Functions You saw one example of a function above. Here are some more: > (+ 3 4 5 6) ;this function takes any number of arguments 18 > (+ (+ 3 4) (+ (+ 4 5) 6)) ;isn't prefix notation fun? 22 > (defun foo (x y) (+ x y 5)) ;defining a function FOO > (foo 5 0) ;calling a function 10 > (defun fact (x) ;a recursive function (if (> x 0) (* x (fact (- x 1))) 1)) FACT > (fact 5) 120 > (defun a (x) (if (= x 0) t (b (- x)))) ;mutually recursive functions A > (defun b (x) (if (> x 0) (a (- x 1)) (a (+ x 1)))) B > (a 5) T > (defun bar (x) ;a function with multiple statements in (setq x (* x 3)) ;its body -- it will return the value (setq x (/ x 2)) ;returned by its final statement (+ x 4)) BAR > (bar 6) 13 When we defined foo, we gave it two arguments, x and y. Now when we call foo, we are required to provide exactly two arguments: the first will become the value of x for the duration of the call to foo, and the second will become the value of y for the duration of the call. In LISP, most variables are lexically scoped; that is, if foo calls bar and bar tries to reference x, bar will not get foo's value for x. The process of assigning a symbol a value for the duration of some lexical scope is called binding. You can specify optional arguments for your functions. Any argument after the symbol &optional is optional: > (defun bar (x &optional y) (if y x 0)) BAR > (defun baaz (&optional (x 3) (z 10)) (+ x z)) BAAZ > (bar 5) 0 > (bar 5 t) 5 > (baaz 5) 15 > (baaz 5 6) 11 > (baaz) 13 It is legal to call the function bar with either one or two arguments. If it is called with one argument, x will be bound to the value of that argument and y will be bound to nil; if it is called with two arguments, x and y will be bound to the values of the first and second argument, respectively. The function baaz has two optional arguments. It specifies a default value for each of them: if the caller specifies only one argument, z will be bound to 10 instead of to nil, and if the caller specifies no arguments, x will be bound to 3 and z to 10. You can make your function accept any number of arguments by ending its argument list with an &rest parameter. LISP will collect all arguments not otherwise accounted for into a list and bind the &rest parameter to that list. So: > (defun foo (x &rest y) y) FOO > (foo 3) NIL > (foo 4 5 6) (5 6) Finally, you can give your function another kind of optional argument called a keyword argument. The caller can give these arguments in any order, because they're labelled with keywords. > (defun foo (&key x y) (cons x y)) FOO > (foo :x 5 :y 3) (5 . 3) > (foo :y 3 :x 5) (5 . 3) > (foo :y 3) (NIL . 3) > (foo) (NIL) An &key parameter can have a default value too: > (defun foo (&key (x 5)) x) FOO > (foo :x 7) 7 > (foo) 5 Printing Some functions can cause output. The simplest one is print, which prints its argument and then returns it. > (print 3) 3 3 The first 3 above was printed, the second was returned. If you want more complicated output, you will need to use format. Here's an example: > (format t "An atom: ~S~%and a list: ~S~%and an integer: ~D~%" nil (list 5) 6) An atom: NIL and a list: (5) and an integer: 6 The first argument to format is either t, nil, or a stream. ...
stec007