LevSelector.com |
Lisp tutorial
intro | home - top of the page - |
Good Common LISP for Windows Interpreter: Corman
Lisp 1.3
www.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html
- download "Common Lisp the Language" by Guy L. Steele, 2nd ed., 1990.
Why should you learn LISP? The short answer is you shouldn't, you are
being made to learn it in your Computer Science course. There are very
few jobs in LISP outside of Universities and research centers, so it probably
isn't going to make you a whole lot of money. Bear in mind though, LISP
is the language of Artificial Intelligence, and if AI makes it really big
it could be a handy language to know. Luckily LISP is quite easy to learn,
even if you don't know much about programming. It actually can be quite
fun as well, because you just type a command into the interpreter and you
can have it evaluated immediately. The best way to learn LISP is to know
how to do the examples below. Once you learn a few basics the rest of the
language opens up to you.
Basic Primitives | home - top of the page - |
This section introduces you to basic symbolic manipulation primitives. Some primitives used work on numbers, others work on lists.
Addition, multiplication etc is performed as follows:
(+ 5 17) : equivalent to 5 + 17
(* 2 3 5) : equivalent to 2 * 3 * 5
Function calls can also be used in place of arguments:
(+ (- 5 2) (* 2 3 5)) : equivalent to (5 - 2) + (2 * 3 * 5)
+ - etc are called primitives. These ones can work with any number of arguments. The numbers these things have been manipulating are called numeric atoms.
Creating sets (actually, creating variables) can be done with setf : this creates a set called learned with three members:
(setf learned '(VB C++ LISP))
The apostrophe is uses to designate that something in brackets isn't a function (or an S-expression). Basically, if LISP receives something like (VB C++ LISP) it assumes VB is the name of a function and C++ and LISP are its arguments. The apostrophe says that this isn't a function, it's a list, and can itself be an argument in a function.
now when you type
learned
the list will be displayed.
We can make another set called notlearned:
(setf notlearned '(Cobol Java Fortran))
Now we have two sets. We can remove an element from one set like this:
(setf notlearned (remove 'Java notlearned))
And add an element to a list like this:
(setf learned(cons 'Java learned))
You can also make more than one list at a time:
(setf lista '(A B C)
listb '(D E F))
This returns (D E F) but both lists are created.
Basic list Functions:
(first '(a b c d)) : this returns A
(rest '(a b c d)) : this returns (B C D)
First and Rest always return a list.
The following examples give a better idea of how these functions work in more complex situations:
(rest '(a)) : this one element list returns NIL
(first ()) : an empty list passed to first or rest will return NIL
(first '((A B) (C D))) : this returns (A B)
(first (rest '(A B C D))) : this returns B
Note also, that
(second '(A B C D))
will return the same thing. Primitives FIRST .. TENTH are part of the language.
Remember though, the apostrophe placement is critical:
(first '(rest (A B C))) : this will return REST because it thinks that rest is an element in the list.
We can also use first and rest on the sets we made with setf above:
(first learned)
(rest notlearned)
Cons takes an expression and a list, and returns a new list where the first element is the expression:
(cons 'z '(a b c d)) : this returns (Z A B C D)
The expression can also be a list (but look carefully at the returned list).
(cons '(x y z) '(a b c)) : this returns ((X Y Z) A B C)
Append is similar, but it combines the elements of two or more lists in a new list:
(append '(x y z) '(a b c)) : this returns (X Y Z A B C)
You can also use your own sets:
(append learned notlearned)
The List primitive is a little different again, it makes a list out of its arguments:
(list '(A B C) '(D E F) '(G H I)) : this will return ((A B C) (D E F) (G H I))
It is important to remember that CONS, APPEND and LIST do not alter the values of their arguments (the lists they send to the primitives).
If you want to add an element to a list you do what we did above:
(setf learned(cons 'Java learned))
NTHCDR is like REST, except you can specify how many elements it chops of the front:
(nthcdr 3 '(A B C D E F)) : this returns (D E F) : three elements have been trimmed off.
BUTLAST is similar again, but it trims the elements of the end:
(butlast '(A B C D E F) 3) : this returns (A B C) : note the number is at the end this time.
LAST returns the last elements:
(last '(A B C D E)) : returns (E)
LENGTH returns the number of top level elements:
(length '((A B) (C D))) : this returns 2.
REVERSE reverses the order of the elements:
(reverse '((A B) (C D))) : this returns ((C D) (A B))
Another basic primitive are:
(print 10) : prints out 10 10
It is also possible to create a global variable like this:
(defvar *my_name* 'Dane)
By convention global variables are inside asterisks. These should be
at the top, not inside functions.
LISP Basic Data Types | home - top of the page - |
Three of the most popular data types are integers, ratios and floating-point. Ratios represent something like 2/3. Integers are whole numbers, but they can have a point after them. Floating point, of course are numbers like 5.432.
The result of division depends on the numbers involved.
Two floating point numbers return another floating point number.
Two integers that don't divide evenly will return a ratio:
(/ 31 5) : this returns 31/5.
but you can get a floating point result like this:
(float (/ 31 5)) : this returns 6.2.
or you can get an integer result like this:
(round (/ 31 5)) : this returns
6
LISP Structures | home - top of the page - |
You can also define a structure in LISP:
(defstruct person
name
age)
: This has made a type, not any instance of it. We can use a
function that will make instances though.
(setf P1
(make-person
:name 'Dane
:age 18))
And then access it like this:
(person-age p1)
(person-name p1)
Values in the structure can also be changed:
(setf (person-name p1) 'Jerry)
To test if something is an example of the person structure use this person-p:
(person-p p1) : this returns T
More Number Orientated Primitives | home - top of the page - |
MIN and MAX are self explanatory:
(MIN 3 5 7 1) : this returns 1
(MAX 3 5 7 1) : this returns 7
EXPT raises a number to a power:
(expt 2 4) : this returns 16
SQRT returns the square root:
(sqrt 9) : this returns 3
ABS returns the absolute value :
Defining Your Own Functions | home - top of the page - |
To define your own procedure you use the special form called defun
(defun second-element (arg) : arg is the element the function takes
(first (rest arg)))
We can now use this as follows
(second-element '(a b c d e)) : this will print out B
This function adds two arguments:
(defun addition(arg1 arg2)
(+ arg1 arg2))
(addition 2 4) : this prints out 6
There is no real point to these two procedures, they just reproduce second and + respectively. It does show the basic formula though. This could all be put on the same line, but it is easier to split it up. On the first line we first use the defun keyword to show we are making our own procedure. Next we give this procedure a name, and follow that with an argument list - the names for the variables should be kept relevant.
We then define what the procedure does with these variables on the following line(s). Remember to keep track of all the brackets, they can start to build up.
Here is another one that returns the first and last elements in a list:
(defun firstlast(arg)
(cons(first arg)
(last arg)))
We can then call the function like this:
(firstlast '(A B C D)) : which will return (A D).
Or we could use a list made by setf:
(setf month '(JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC))
(firstlast month) : which returns (JAN DEC).
Remember, this hasn't changed the value of month, it has returned a new set.
Or we can make it accept two sets and return the first from one and the last from the other
(defun firstlast(arg1 arg2)
(cons(first arg1)
(last arg2)))
(firstlast '(A B C) '(D E F)) : which returns (A F).
LET is another popular primitive that binds parameters. These only last for the duration of the function, so they are a temporary form of primitives like setf. This is how it works:
(defun firstlast(arg)
(let ((ele1(first arg))
(ele2 (last arg)))
(cons ele1 ele2)))
This is exactly the same function, but the operations are being read
into the variables ele1 and ele2.
LISP Predicates | home - top of the page - |
A predicate returns a value of true or false. In LISP false is always signaled by NIL, and truth by anything else, but often by T.
EQUAL tests to see if two expressions are the same:
(equal (* 3 3) 9) : this returns T.
(equal (* 3 3) 8) : this returns NIL.
There are more predicates like EQUAL which are more specific:
EQL tests to see if two arguments are the same symbol or number:
(eql 7 7.0) : this returns NIL
(eql (+3 4) 7) : this returns T
= tests to see if two arguments are the same number:
(= 7 7.0) : this returns T
MEMBER tests to see if its first argument is in its second argument. Given the set:
(setf tv '(seinfeld friends frasier er happydays))
(member 'frasier tv) : this will return (FRASIER ER HAPPYDAYS) :notice that it returns everything to the right of the element you searched for.
To find a member that happens to be a set you have to do things differently:
(setf characters '((jerry kramer) (rachel monica) (fonzie richie)))
Using just member won't find the elements:
(member '(rachel monica) characters) : this returns NIL
This is because member uses EQL when we need it to use EQUAL. Therefore we have to specify the test to use:
(member '(rachel monica) characters :test #'equal)
This returns ((RACHEL MONICA (FONZIE RICHIE))
Any symbol beginning with a : is a keyword (remember not to have a space between colon and word). The test keyword can be used to modify the natural behavior of member. The #' is used to create what is called a procedural object.
There are several other predicates:
LISTP takes any Lisp data-item, and returns T if it is a list, and NIL otherwise.
ENDP which takes a list, and returns T if the list is empty, NIL otherwise. (This function is also known as NULL.) Note that (endp nil) returns T : nil is an empty set.
NUMBERP tests to see if it is a number.
SYMBOLP tests to see if it is a symbol.
There are quite a few number predicates. A few of them are:
ZEROP : is it zero
PLUSP and MINUSP : is it positive or negative
EVENP and ODDP : is it even or odd
> and < : Are they in descending or ascending order.
Two combine the results of more than one predicate in a test use the words AND and OR.
Suppose we have these two sets:
(setf comedy '(frasier happydays allymcbeal seinfeld))
(setf drama '(er allymcbeal nypdblue))
If we want to find if something is a member of both we use:
(and (member 'allymcbeal comedy) (member 'allymcbeal drama)) : this will return (ALLYMCBEAL NYPDBLUE)
Or if we want to know if something is in one or the other:
(or (member 'seinfeld comedy) (member 'seinfeld drama)) : this
will return (SEINFELD)
LISP Conditionals | home - top of the page - |
The IF form is a common structure from other languages. In LISP it looks like this:
(if (listp comedy) 'full 'empty) : returns FULL
This expression contains a test form (listp), something to evaluate (comedy), something to return if the result is true (full) and something if false (empty)
The WHEN structure is similar, but it only does something if the condition is true:
(setf hightemp 80 todaytemp 90)
(when (> todaytemp hightemp)
(setf hightemp todaytemp)
'today_had_highest_temp)
When the temperature of todaytemp is higher than hightemp, hightemp is changed to todaytemp and a message is printed. Otherwise NIL is displayed.
If you only want to do something in place of NIL, use the UNLESS structure:
(unless (> todaytemp hightemp)
'today_was_not_the_hightest)
Now if it would normally evaluate to NIL the message is printed, otherwise NIL is printed.
COND is a more versatile conditional structure.
(setf temp 80)
(cond((> temp 100) 'very_hot)
((> temp 85) 'hot)
((> temp 75) 'quite_hot)
((> temp 60) 'mild)) : this returns QUITE_HOT
Once it got down to something that is true, the corresponding consequent is triggered. No more conditions are evaluated after that.
This could then be put in a user defined procedure:
(defun today (temp)
(cond((> temp 100) 'very_hot)
((> temp 85) 'hot)
((> temp 75) 'quite_hot)
((> temp 60) 'mild)))
and then called like this:
LISP Recursion | home - top of the page - |
A lot of people only know one thing about recursion : there is always a way of doing a recursive function non-recursively. Simple recursion isn't too difficult though, just remember that there must be a condition that stops the recursion, or it will go on for ever.
Here is a simple function that takes two numbers and raises the first to the power of the second:
(defun raise(num1 num2)
(if (zerop num2)
1
(* num1 (raise num1 (- num2 1)))))
This is quite simple. Raise excepts two integers. It then goes into a loop - if the second number is 1 then the recursion stops, otherwise raise is called again taking 1 of the second number.
The other example always used to demonstrate recursion is Fibonacci numbers: for number 4 we want to find (4 + 3 + 2 +1).
The recursive function for this is:
(defun Fibonacci (num)
(if (= num 1)
1
(+ num (Fibonacci(- num 1)))))
We can then find out that (fibonacci 4) gives 10.
These are just two basic examples, but many procedures can be done recursively,
usually in an effort to make them faster and easier to read.
LISP Iteration | home - top of the page - |
Iteration, usually performed with the for keyword in other languages, can also be done in LISP. In LISP the primitive is called DOTIMES.
(defun power (num1 num2)
(let ((result 1))
(dotimes (count num2 result)
(setf result (* num1 result)))))
We can then use it :
(power 2 3) : which gives 8
It is not so simple on the face of it. The first line is familiar enough. In the second line we create the variable that will hold the result and give it the value of 1 to begin with.
The dotimes statement takes three parameters. The first is the count parameter. This is followed by the upper limit form and finally the result form.
The final line is the body of the loop. The first time through the loop result becomes equal to 2 (2 * result). The second time through it takes the value of 4 (2 * result (which was now 2)). The final time through it takes the value of 8 (2 * result (which had been made 4)). The result is then printed.
Here is another example:
(dotimes (c 4 'done)
(princ "Counter =")
(princ c)
(terpri))
This prints out:
Counter =0
Counter =1
Counter =2
Counter =3
DONE
The terpri command provides a carriage return.
The DO primitive is even more difficult. This is how the example above can be done with the DO loop:
(defun dopower (num1 num2)
(do ((result 1)
(exponent num2))
((zerop exponent) result)
(setf result (* num1 result))
(setf exponent (- exponent 1))))
This does work, copy and paste it into your interpreter and see, but it is nowhere near as easy as a do loop in most other languages. This is how it works:
The first part of a do loop contains a list of parameters that are given initial values on entering the loop. If these variables had values before the loop, these values are restored after the loop. If there are no parameters an empty list must be placed in the line instead.
The second part of the loop, ((zerop exponent) result), contains the terminating condition, and what will be returned.
The rest of the loop describes what happens if the termination test isn't met.
You can break out of a loop prematurely with the keyword RETURN
Iteration isn't a whole lot of fun in LISP.
LISP Printing | home - top of the page - |
The primitive PRINT can be used to print a single element:
(setf today 'monday)
(print today)
This will printout MONDAY twice.
(print (- 10 5)) : this prints out 5 5
The FORMAT primitive lets you do more complex printing. Strings are data types in LISP. They are characters surrounded by double quotes. We can use format to print strings:
(format t "Hello World") : the t stands for terminal. Notice that it puts NIL at the end.
You can indicate you want a fresh line with ~% :
(format t "~%Hello ~%World")
Another useful directive is ~a which lets you put something after the string:
(setf temp 100)
(format t "The temperature is ~a" temp)
There are a lot more directives that format takes. If you want to know
what they are, go to section 22.3.3 of Common
LISP
LISP Reading | home - top of the page - |
READ is used to put information into a program. When it is encountered the program stops and waits for a value. The biggest problem is that READ never bothers to tell you it is waiting for anything, so you should provide a prompt separately.
This is how to read information:
(let ((temp 0))
(print '(Enter the temperature))
(setf temp(read))
(print (append '(The temperature today was) (temp)))))
Beyond the Basics | home - top of the page - |
Congratulations, you are now a geek: go and ring your parents. All the
categories above demonstrate the essential aspects of lisp, but there are
many more features all good geeks should know. There are many quite interesting
things you can do with LISP if you have the patience. It is probably the
best language around for writing Artificial Intelligence applications from
Expert Systems to voice recognition.
LISP Property Lists | home - top of the page - |
A property list is a collection of property names and values associated with a symbol. For instance, the symbol could be a person: Chandler. Then we can have properties associated with this: Parents (Rachel Ross) and Children (Monica Joey).
This is how we do it:
(setf person '(Chandler)) : (returns (CHANDLER))
(setf (get 'chandler 'parents) '(Rachel Ross)) : this returns (RACHEL ROSS)
(setf (get 'chandler 'children) '(Monica Joey)) : this returns (MONICA JOEY)
Retrieving these values is very easy - just remove the setf:
(get 'chandler 'parents)
(get 'chandler 'children)
You can also remove a property:
(remprop 'chandler 'children) : this returns T.
(get 'chandler 'children) : this now returns NIL
That is really all there is to property lists.
LISP Arrays | home - top of the page - |
To create an array you need to tell LISP how many dimensions it has, and how many elements are in each dimension with MAKE-ARRAY:
(setf numarray (make-array 10)) : this makes the one dimensional array numarray with ten elements. This will return #(0 0 0 0 0 0 0 0 0 0 )
(setf twodimension (make-array '(4 4))) : this creates a two dimensional array called twodimension, and returns #2A((0 0 0 0)(0 0 0 0)(0 0 0 0)(0 0 0 0))
You can also initialize the elements as you declare the array.
(setf initarray (make-array 10 :initial-element 20)) : this returns #(20 20 20 20 20 20 20 20 20 20)
To find out what is stored in an element use this structure:
(aref numarray 3)
(aref twodimension 2 4)
You also use the same base structure to write data into the array:
(setf (aref numarray 3) 40)
The dotimes loop is used a lot with arrays: this one counts how many elements in numarray equal to a given number:
(defun equalnum (num)
(let ((result 0))
(dotimes (n 4 result)
(when (equal num (aref numarray n))
(setf result (+ result 1))))))
If it is called like this:
(equalnum 40) : this returns 1.
Macros | home - top of the page - |
A macro is like a function, except that its arguments aren't evaluated when it's called. To demonstrate the difference between functions and macros, consider this this function.
(defun mycons (item list)
(cons item list))
It can then be called like this:
(mycons 'a '(b c)) which returns (A B C)
This function could be written as a macro:
(defmacro mycons (item list)
`(cons ',item ',list))
It can then be called like this:
(mycons a (b c)) which returns (A B C)
A macro uses its arguments to create a LISP expression, and then evaluates
it.
There is never any real need to use macros if you don't want to, functions
can always be used to do the same things. Many people find macros more
convenient in certain situations however.