Learning Lisp Fast - Part 2
(Source and copyright: Sean Luke - George Mason University, cs.gmu.edu/~sean/lisp/LispTutorial2.html)
This tutorial will introduce you to more concepts in Lisp that weren't covered in the Quickstart. In addition to more data structures and control concepts, we'll get into basic concepts that really make Lisp different from other languages.
As before, the examples we give will be based on CLISP, but they work basically the same in all lisp systems.
As before, the table cell below shows what you type, and the output, for this tutorial. Text shown in blue you are responsible for typing, with a Return at the end of the line. Text shown in black indicate stuff that is printed back to you. Text shown in red are remarks -- do not type them.
If the cell is divided by a line, as is shown at right, then this indicates two different examples.
This text is being printed out. You would type this text [This is a remark]
Arrays and Vectors
Lisp has many kinds of arrays: multidimensional arrays, variable-length arrays, fixed-length simple arrays, arrays guaranteed to have certain types in them, arrays which can hold anything, etc.
Lisp arrays are created with the function make-array. The simplest form of this function is:
This form makes a one-dimensional fixed-length array length elements long. The elements are each initialized to nil.
An array of this form is called a simple-vector. You don't just have to use make-array to build a simple-vector. Just as you can make a list of the symbols a b c by typing '(a b c), you can make a simple vector of the symbols a b c by typing #(a b c)
> (make-array 4) #(NIL NIL NIL NIL) > #(a b c) #(A B C) >
A multidimensional array is created as follows:
This form makes an N-dimensional fixed-length array of the dimensions given by elements in the list. The elements are each initialized to nil.
You can specify the initial value of the elements with the keyword :initial-element.
The general function for extracting the element of any array is aref. It takes the form:
(aref array index1 index2 ...)
Simple vectors have a special version, svref, which is slightly faster than aref (in fact, aref just calls svref for simple vectors):
(svref simple-vector index)
Lisp arrays are zero-indexed. This is just like saying (in C++/Java): array[index1][index2]...
Multidimensional arrays can also be specified with #nA(...), where n is the number of dimensions. See the example at right.
> (make-array '(4 3 8)) [it has to be quoted] #3A(((NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL)) ((NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL)) ((NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL)) ((NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL))) > (make-array '(2 2) :initial-element 0) #2A((0 0) (0 0)) > (setf *j* #2A((1 2 3) (4 5 6))) #2A((1 2 3) (4 5 6)) > (aref *j* 1 1) 5 > (aref #(a b c d e) 3) D > (svref #(a b c d e) 3) [ faster ] D
Vectors are one-dimensional arrays. You've already seen fixed-length vectors (known in Lisp as simple-vectors). Lisp also has variable-length vectors.
Variable-length vectors are created with the keywords :adjustable and :fill-pointer in the following fashion:
(make-array length :fill-pointer t :adjustable t)
You can have a zero-length vector. It's very common to start a variable-length array at length 0.
You can tack new stuff onto the end of a variable-length vector with the command vector-push-extend. You can "pop" elements off the end of the variable-length vector with vector-pop.
To use these functions, the vector must be variable-length. You cannot push and pop to a simple vector.
Multidimensional arrays can also have their sizes adjusted. We'll just leave it at that -- look it up if you're interested.
> (setf *j* (make-array 0 :fill-pointer t :adjustable t)) #() > (vector-push-extend 10 *j*) 0 > (vector-push-extend 'hello *j*) 1 > *j* #(10 HELLO) > (aref *j* 1) HELLO > (vector-pop *j*) HELLO > *j* #(10)
A string is, more or less, a vector of characters. You can access elements with aref. But because a string is not a simple vector (oddly enough), you cannot use svref. I have no idea why.
Although string elements can be accessed via aref, strings have their own special function which does the same thing: char, which takes the form:
(char string index)
In most systems, the two functions are about the same speed.
> (aref "hello world" 3) #\l > (char "hello world" 6) #\w
Setf and Friends
setf doesn't just set variables. In general, (setf foo bar) "sees to it" that foo will evaluate to bar. setf can "see to" an amazing number of things.
To set the value of an element in an array (I bet you were wondering about that!) you say
(setf (aref array indices... ) val)
You can do the same trick with svref and char.
You can also use setf to modify lists. However, this is dangerous if you don't know what you're doing. For now, don't do it. Stick with modifying arrays and strings.
> (setf *j* #(a b c d e)) #(A B C D E) > (setf (svref *j* 3) 'hello) HELLO > *j* #(A B C HELLO E) > (setf *k* (make-array '(3 3 3) :initial-element 4)) #3A(((4 4 4) (4 4 4) (4 4 4)) ((4 4 4) (4 4 4) (4 4 4)) ((4 4 4) (4 4 4) (4 4 4)))> (vector-pop *j*) HELLO > (setf (aref *k* 2 1 1) 'yo) YO > *k* #3A(((4 4 4) (4 4 4) (4 4 4)) ((4 4 4) (4 4 4) (4 4 4)) ((4 4 4) (4 YO 4) (4 4 4))) > (setf *l* "hello world") "hello world" > (setf (char *l* 4) #\B) #\B > *l* "hellB world"
A variant of setf called incf does more or less the same thing as the ++ operator in C++ or Java, except that it works on all sorts of things (array slots, etc.) in addition to just variables. The form:
(incf expression 4)
...will see to it that expression evaluates to 4 more than it used to (by adding 4 to it). If you just say:
...this by default sees to it that expression evaluates to 1 more than it used to.
The macro decf does the opposite.
> (setf *j* #(1 2 3 4 5)) #(1 2 3 4 5) > (incf (svref *j* 3) 4) 8 > *j* #(1 2 3 8 5) > (setf *k* 4) 4 > (incf *k*) 5 > *k* 5 > (decf *k* 100) -95
Another variant of setf called push can be used to "see to it" that an expression (which must evaluate to a list) now evaluates to a list with an element tacked onto the front of it. If you say:
(push val expression)
...this is roughly the same as saying
(setf expression (cons val expression))
You can also "see to it" that a list has an element removed from the front of it with pop:
> (setf *j* #((a b) (c d) (e f))) [ a simple-vector of lists ] #((A B) (C D) (E F)) > (push 'hello (svref *j* 1)) (HELLO C D) > *j* #((A B) (HELLO C D) (E F)) > (setf *k* '(yo yo ma)) (YO YO MA) > (pop *k*) YO > *k* (YO MA)
Another useful variant, rotatef, can be used to swap several elements.
(rotatef expresion1 expression2 ... expressionN)
...this is roughly the same as saying
(setf tempvar expression1)
(setf expressionN tempvar)
A simple use of this is simply (rotatef expression1 expression2) which sees to it that the values of expression1 and expression2 are swapped.
> (setf *j* #(gracias senor)) #(GRACIAS SENOR) > (setf *k* 'hello) HELLO > (rotatef (elt *j* 0) (elt *j* 1) *k*) NIL > *j* #(SENOR HELLO) > *k* GRACIAS > (setf *z* #(1 2 3 4 5)) #(1 2 3 4 5) > (rotatef (elt *z* 1) (elt *z* 4)) [ swap 'em ] NIL > *z* #(1 5 3 4 2)
Function, Funcall, and Apply
In Lisp, pointers to functions are first-class data objects. They can be stored in variables, passed into arguments, and returned by other functions.
The special form function will return a pointer to a function. It takes the form (function function-symbol). Notice that just like quote, function doesn't evaluate its argument -- instead it just looks up the function by that name and returns a pointer to it.
Also like quote, function is so common that there is a shorthand for it: a pound sign followed by a quote at the beginning of the function name:
...is the same as...
Keep in mind that you can only get pointers to functions, not macros or special forms.
> (function print) #<SYSTEM-FUNCTION PRINT> > (function if) ["if" isn't a function -- it's a macro] *** - FUNCTION: undefined function IF 1. Break > :a > (setf *temp* (function *)) [the "*" function] #<SYSTEM-FUNCTION *> > *temp* #<SYSTEM-FUNCTION *> > #'print #<SYSTEM-FUNCTION PRINT> > (setf *temp* #'*) #<SYSTEM-FUNCTION *> > *temp* #<SYSTEM-FUNCTION *>
A common mistake among Lisp newbies is to think that variables with function pointers stored in them can be used to make a traditional function call by sticking the variable at the beginning of a list.
Remember that the first item in an evaluated list must be a symbol which is not evaluated. If a variable could be put as the first item, it would have to be evaluated first (to extract the function pointer).
Thus, Common Lisp can associate a function with a symbol (by using defun) and it can also associate a value with the same symbol as a variable (by using setf). A Lisp which can associate two or more different kinds of things at the same time with a symbol is called a Lisp 2. Common Lisp is a Lisp 2. Emacs Lisp is also a Lisp 2.
Scheme, another popular Lisp dialect, evaluates the first item in the list as a variable, looking up its function-pointer value. Scheme associates only one thing with a symbol: the item stored in its variable. Thus Scheme is a Lisp 1.
Lisp 1's are simpler and more intuitive than Lisp 2's. But it is more difficult to do certain kinds of powerful things with them, like macros. We'll get to that later on.
> (setf *new-print* (function print)) #<SYSTEM-FUNCTION PRINT> > (*new-print* "hello world")> *** - EVAL: the function *NEW-PRINT* is undefined 1. Break > :a >
If you can't just call a function pointer by sticking it in the first spot in a list, how do you call it?
There are a great many functions and macros which use function pointers. One basic one is funcall. This function takes the form
(funcall function-pointer arg1 arg2 ... )
funcall is a function which evaluates function-pointer, which returns a pointer to a function, then it evaluates each of the arguments, then passes the argument values into the function. funcall returns the value of the function.
> (setf *new-print* (function print)) #<SYSTEM-FUNCTION PRINT> > (funcall *new-print* "hello world")> "hello world" "hello world" > (funcall #'+ 1 2 3 4 5 6 7) 28 > (funcall #'funcall #'+ 1 2 3 4 5 6 7) [hee hee!] 28 >
Another useful function which takes function pointers is apply. The simple version of this function takes the form
(apply function-pointer list-arg )
apply takes a function pointer, plus one more argument which must evaluate to a list. It then takes each element in this list and passes them as arguments to the function pointed to by function-pointer. apply then returns the value the that the function returned.
It so happens that apply can do one additional trick. Alternatively, apply can look like this:
(apply function-pointer arg1 arg2 ... list-arg )
The last argument must evaluate to a list. Here, apply builds a list before passing it to the function. This list is built by taking each of the arg1, arg2, arguments and concatenating their values to the front of the list returned by list-arg. For example, in (apply #'+ 1 2 3 '(4 5 6)), the concatenation results in the list '(1 2 3 4 5 6). Thus
(apply #'+ 1 2 3 '(4 5 6)) is the same thing as
(apply #'+ '(1 2 3 4 5 6)) which is the same thing as
(apply #'+ 1 2 3 4 5 6 '()) which of course is the same thing as
(apply #'+ 1 2 3 4 5 6 nil)
> (apply #'+ '(1 2 3 4 5 6)) 21 > (apply #'+ 1 2 3 4 5 6 nil) 21 > (apply #'+ 1 2 3 '(4 5 6)) 21 > (apply #'apply #'+ '(1 2 3 (4 5 6))) [woo hoo!] 21 > (apply #'funcall #'+ '(1 2 3 4 5 6)) [yee haw!] 21 > (funcall #'apply #'+ '(1 2 3 4 5 6)) [hmmmm...] 21
Lisp uses pointers to functions everywhere. It's what makes Lisp's built-in functions so powerful: they take optional functions which let you customize the built-in ones in special ways.
One very common use of pointers to functions is mapping. Mapping applies a function repeatedly over one or more lists, resulting in a new list. The most common mapping function is mapcar, which in a basic form looks like this:
(mapcar function-pointer list)
Since we're providing just one list, function-pointer must be a pointer to a function which can take just one argument, for example, sqrt.
In this form, mapcar repeatedly applies the function to each element in the list. The return values are then put into a list and returned.
> (mapcar #'sqrt '(3 4 5 6 7)) (1.7320508 2 2.236068 2.4494898 2.6457512) > (mapcar (function print) '(hello there how are you)) HELLO THERE HOW ARE YOU (HELLO THERE HOW ARE YOU) >
mapcar more generally looks like this:
(mapcar function-pointer list1 list2 ...)
If function-pointer points to a function which takes N arguments, then we must provide N lists.
mapcar takes the first element out of each list and passes them as arguments to the function. mapcar then takes the second element out of each list and passes them as arguments to the function. And so on. mapcar then returns a list of the return values of the function.
If any list is shorter than the others, mapcar operates only up to the shortest list and then stops.
Lisp provides a number of other useful mapping functions: map, mapc, mapcan, mapcon ...
> (mapcar #'/ '(1 2 3 4 5) '(7 8 9 10 11)) (1/7 1/4 1/3 2/5 5/11) > (mapcar #'* '(1 2 3 4) '(5 6) '(7 8 9)) [one list is only 2 long] (35 96) >
A related feature is reduction: composing a function in on itself. The basic reduce function looks similar to mapcar:
(reduce function-pointer list)
function-pointer must point to a function which takes exactly two arguments. If the elements in list are a b c d, and the function func is stored in the function pointer, this is the same thing as doing:
(func (func (func a b) c) d)
You can also change the order of operations with the :from-end t keyword argument, resulting in the ordering:
(func a (func b (func c d)))
reduce has other gizmos available. Check 'em out.
[Note: (expt a b) computes a ^ b (a to the power of b) ] > (reduce #'expt '(2 3 4 5)) [((2^3)^4)^5)] 1152921504606846976 > (reduce #'expt '(2 3 4) :from-end t) [2^(3^4)] >
Lambda and Closures
A lambda expression is one of the more powerful concepts in Lisp. A lambda expression is an anonymous function, that is one that doesn't have a name -- just a pointer to it.
Lambda expressions are created using the form:
(function (lambda (args...) body...))
Note how similar this is to defun:
(defun function-name (args...) body...)
A lambda expression builds a function just like defun would, except that there's no name associated with it. Instead, the lambda expression returns a pointer to the function.
Remember that function has a shorthand of #' so the lambda expression is usually written like this:
#'(lambda (args...) body...)
To make things even more confusing, Common Lisp has provided for you an actual macro called lamda, which does exactly the same thing. Thus if you really want to (but it's not good style) you can write it as just:
(lambda (args...) body...)
> (mapcar #'(lambda (x) (print (* x 2))) '(1 2 3 4 5 6)) 2 4 6 8 10 12 (2 4 6 8 10 12) > (reduce #'(lambda (a b) (/ a (* b b))) '(2 3 4)) [(2 / 3^2) / 4^2] 1/72 > (funcall #'(lambda (a b) (/ a (* b b))) 9 7) 9/49 [Not too useful this one, just an example...]
Lambda expressions are useful when you need to pass in a quick, short, temporary function. But there is another very powerful use of lambda expressions: making closures.
A closure is a function bundled together with its own lexical scope. Usually you can think of this as a closure being a function plus its own personal, private global variables.
When a function is built from a lambda expression, it is usually created in the context of some outer local variables. After the function is built, these variables are "trapped" with the lambda expression if anything in the lambda expression referred to them. Since the lambda expression is hanging on to these variables, they're not garbage collected when the local scope is exited. Instead they become private variables that only the function can see.
We can use this concept to make function-building functions. Consider:
(defun build-a-function (x) #'(lambda (y) (+ x y)))
...examine this function carefully. build-a-function takes a value x and then returns a function which adds that amount x to things!
> (defun build-a-function (x) #'(lambda (y) (+ x y))) BUILD-A-FUNCTION > (setf *+3* (build-a-function 3)) #<CLOSURE :LAMBDA (Y) (+ X Y)> > (funcall *+3* 9) 12 > (funcall *+3* 2) 5 > (setf *-6* (build-a-function -6)) #<CLOSURE :LAMBDA (Y) (+ X Y)> > (funcall *-6* 21) 15  (funcall *-6* (funcall *+3* 38)) 35
Closures are also common when we need to make a quick custom function based on information the user provided. Consider:
(defun add-to-list (val list-of-numbers) (mapcar #'(lambda (num) (+ val num)) list-of-numbers))
...examine this function carefully as well. add-to-list takes a number val and a list of numbers. It then maps a custom function on the list of numbers. This custom function adds val to each one. The new list is then returned.
Notice that the lambda expression is converted into a function even though it refers to val inside the lambda expression.
Closures are examples of powerful things which C++ simply cannot do. Java gets there part-way. Java can do lambda expressions in the form of "anonymous classes". But it too cannot do real closures, though there are nasty hacks to work around the issue.
> (defun add-to-list (val list-of-numbers) (mapcar #'(lambda (num) (+ val num)) list-of-numbers)) ADD-TO-LIST > (add-to-list 4 '(1 2 3 4 5)) (5 6 7 8 9) [ Here's the more C++ way to do it... ] > (defun icky-add-to-list (val list-of-numbers) (let (bag) (dolist (x list-of-numbers) (push (+ val x) bag)) (reverse bag))) ICKY-ADD-TO-LIST > (icky-add-to-list 4 '(1 2 3 4 5)) (5 6 7 8 9)
Closures also occur with defun. Imagine if defun were called inside a let statement:
(let ((seed 1234)) (defun rand () (setf seed (mod (* seed 16807) 2147483647))))
Here we defined a local variable called seed. Inside this local environment, we defined a function called rand which uses seed. When we leave the let, what happens to seed? Normally it would get garbage collected. But it can't here -- because rand is holding on to it. seed becomes a private global variable of the function rand. No one else can see it but rand.
You can use this for other interesting purposes. Imagine that you want to make a private bank account:
(let ((account 0)) (defun deposit ($$$) (setf account (+ account $$$))) (defun withdraw ($$$) (setf account (- account $$$))) (defun amount () account))
The functions deposit, withdraw, and amount share a common private variable called account that no one else can see.
This isn't much different from a Java or C++ object with a private instance variable and three methods. Where did you think object-oriented programming came from? You got it.
In fact, Lisp can be easily modified to do rather OOP built on top of closures. It comes with an OOP system, CLOS, as part of the language (though I think CLOS is too mammoth, so I usually make my own little OOP language in Lisp instead).
> (let ((seed 1234)) (defun rand () (setf seed (mod (* seed 16807) 2147483647)))) RAND > (rand) > (rand) > (rand) > seed *** - EVAL: variable SEED has no value 1. Break > :a > (let ((account 0)) (defun deposit ($$$) (setf account (+ account $$$))) (defun withdraw ($$$) (setf account (- account $$$))) (defun amount () account)) AMOUNT > (deposit 42) 42 > (withdraw 5) 37 > (amount) 37 > account *** - EVAL: variable ACCOUNT has no value
Vectors (both simple and variable-length), lists, and strings are all sequences. Multidimensional arrays are not sequences.
A function which works with any kind of sequence is a sequence function (duh). We've seen some examples of sequence functions before: length, reverse, subseq.
Another common sequence function is elt, of the form:
(elt sequence index)
elt returns element #index in the sequence. You can use elt inside setf to set the element (again, don't change elements in lists unless you know what you're doing. Strings and vectors are fine).
elt is an example of a general function: it works with a variety of data types, but as a result is slower than custom-made functions for each data type. For example, if you know your sequence is a string, aref is probably faster. If you know your sequence is a simple-vector, svref is much faster. Lists also have a faster function: nth.
> (elt "hello world" 4) #\o > (elt '(yo yo yo whats up?) 4) UP? > (elt #(yo yo yo whats up?) 4) UP?
copy-seq makes a duplicate copy of a sequence. It does not copy the elements (both sequences will point to the same elements).
concatenate concatenates copies of sequences together, producing a new sequence of a given type. The original sequences can be different types. concatenate looks like this:
(concatenate new-sequence-type sequences... )
new-sequence-type is a quoted symbol representing the type of the new sequence. For example, simple vectors use 'simple-vector and lists use 'list and strings use 'string.
make-sequence builds a sequence of a given type and length. Like elt, it is a general function (it calls faster, more type-specific functions underneath). It looks like this:
(make-sequence sequence-type length )
make-sequence has a keyword argument :initial-element which can be used to set the initial element of the sequence.
concatenate and make-sequence show the first examples of type symbols. We'll talk about types more later.
> (copy-seq "hello world") "hello world" [ the copied string ] > (concatenate 'string '(#\y #\o) #(#\space) "what's up?") "yo what's up?" > (make-sequence 'string 4 :initial-element #\e) "eeee"
A host of sequence-manipulative functions have very similar forms.
First off, most sequence-manipulative functions are either destructive or non-destructive. That is, either they modify or destroy the original sequence to achieve their goals (faster), or they make a copy of the sequence first. We'll show the non-destructive versions first.
Second, a great many sequence functions have three versions, the function, the -if version, and the -if-not version. For example, the count function also has count-if and count-if-not. The forms look like this:
(count object sequence keywords...) counts the number of times object appears in sequence.
(count-if test-predicate sequence keywords...) counts the number of times in which test-predicate (a function pointer) returns true for elements in sequence.
(count-if-not test-predicate sequence keywords...) counts the number of times in which test-predicate (a function pointer) returns false (nil) for elements in sequence.
Third, many such functions take a lot of optional keyword arguments. Before testing to see if an element is the one we're looking for, these functions give you a chance to "extract" the relevant item out of the element with an optional function passed in with the keyword argument :key. You can tell the system to scan backwards with :from-end t. You can tell the system to only scan from a certain location to another location in the sequence with the keywords :start and :end. There are other keywords as well.
Other functions which follow this pattern include: find (returns the first element matching the pattern) else nil, position (returns the index of the first element matching the pattern) else nil, remove (removes all the elements matching the pattern from a copy of the sequence), and substitute (replaces all the elements matching the pattern with some other element). substitute has an additional argument indicating the item to replace stuff with, thus its three versions substitute, substitute-if, and substitute-if-not start like this:
(substitute[-if[-not]] thing-to-replace-with rest-of-arguments-as-before...)
> (count #\l "hello world") 3 [ count the number of vowels in "hello world" ] > (count-if #'(lambda (i) (find i "aeiou")) "hello world") 3 [ count the number of non-alpha-chars in "hello world4" ] > (count-if-not #'alpha-char-p "hello world4") 2 [ remove the alpha chars from "hello world4" ] > (remove-if #'alpha-char-p "hello world4") " 4" [ find the first element < 4 in #(4 9 7 2 1 0 3) ] > (find-if #'(lambda (x) (< x 4)) #(4 9 7 2 1 0 3)) 2 [ give the index of the first element < 4 in #(4 9 7 2 1 0 3) ] > (position-if #'(lambda (x) (< x 4)) #(4 9 7 2 1 0 3)) 3 [ replace with NUMBER all the numbers in the list ] > (substitute-if 'number #'numberp '(a b 3 d "yo" 4.2 e)) (A B NUMBER D "yo" NUMBER E) [ replace PI with 3.14159 for elements #4 through #9 ] > (substitute 3.14159 'pi '(pi a b pi c d e pi f pi pi g h pi) :start 4 :end 10) (PI A B PI C D E 3.14159 F 3.14159 PI G H PI)
Another useful function, search, searches for the first index where one subsequence appears in another sequence. It takes the form:
(search subsequence sequence keywords...)
Keywords include :key, :test, :test-not, :from-end, :start1, :end1, :start2, :end2. Try them out and see what they do.
> (search "wor" "hello world") 6
Many sequence functions have destructive counterparts which are faster but may modify the original sequence rather than making a copy first and modifying the copy.
There are no promises with destructive functions: they may or may not modify the original. They may or may not modify the original into the form you're hoping for. The only guarantee they make is that the value they return will be what you're hoping for. Thus you should only use them on data that you don't care about any more.
The destructive form of remove[-if[-not]] is delete[-if[-not]].
The destructive form of substitute[-if[-not]] is nsubstitute[-if[-not]].
The destructive form of reverse is nreverse.
sort is destructive. Its basic form looks like this:
(sort sequence predicate)
> (setf *j* "hello world") "hello world" > (substitute #\Q #\l *j*) "heQQo worQd" > *j* "hello world" > (nsubstitute #\Q #\l *j*) "heQQo worQd" > *j* "heQQo worQd" > (sort '(4 3 5 2 3 1 3) #'>) (5 4 3 3 3 2 1)
Functions With Variable Arguments
Now that we have enough functions to extract the elements of a list, we can talk about how to make a function which takes a variable number of arguments. The special term &rest, followed by an parameter name, can appear at the end of a parameter list in defun, musch as &key and &optional can appear.
If a function call provides any extra arguments beyond those defined in the parameter list, the additional arguments are all placed in a list, which the &rest parameter is set to. Otherwise it is set to nil.
Though it's possible to have rest-parameters along with keyword parameters and optional parameters in the same function, don't do it. Ick.
> (defun mean (first-num &rest others) [ others will be the list with remaining variable arguments ] "Returns the mean of a bunch of numbers. There must be at least one number." (let ((nums (cons first-num others))) ; nums is list of the numbers (/ (apply #'+ nums) (length nums)))) MEAN > (mean 10 2 3 4) 19/4 > (mean 10) 10 > (mean) *** - EVAL/APPLY: too few arguments given to MEAN >