CS 188, Fall 2005, Introduction to Artificial Intelligence
Lisp tutorial

This tutorial (originally written by Kamin Whitehouse) will walk you through setting up the CS188 Lisp environment step-by-step. There will be a workshop based on this tutorial in 271 Soda, times TBD. The workshop will also be a chance to ask questions about Lisp and ask for help running the solutions to assignment 0.


First things first: get an instructional account unless you have one already. To get an account you will need an account form, which should be available shortly (see the class web page for updates). You need an instructional account to submit your assignment.

The Programming Environment

Working in the labs:  I highly recommend using the Lisp environment I describe here.  If you use the instructional machines you will be on the same step as everybody else and any weird problems you have will probably be answered in the class newsgroup.  Furthermore, if you work in the lab there will be other students working on the same assignments. See the list of labs for more info

Working at home on the instructional machines:  If you want to work from home and still use the instructional machines, I would recommend downloading a copy of SSH (Secure SHell) and SSHing into these machines (it's like telnet, but secure).  Then run emacs in your shell and follow the instructions below.  If you have Hummingbird's Exceed, create a SSH "profile" to connect to your instructional account and set the X11 Tunneling option 'on' and leave the Exceed XServer running in the background.  Now, when you run emacs it will pop up as a new Xwindow.

Working on your personal machine:  You can, of course, download a student version of Allegro Common Lisp from franz.com and get a PC version of emacs, but if you work on an instructional account you might have better luck finding answers to your questions.

Using Lisp within Emacs

Life is much easier if you know how to run Lisp within emacs.  In short, emacs is a program that allows you to interact with data in buffers.  Some buffers can hold data which is read and written to files (such as your source code).  Other buffers can hold data that is read and written to programs (such as your Lisp interpreter or unix shell).

(A quick note about emacs command notation used below.  M-x means Esc-x (press Esc then x) or Alt-x (hold Alt and press x).  C-x means Ctrl-x (hold Ctrl and press x).)

You can control the data in the buffers with emacs commands.  To learn all the emacs commands, read the manual.  Here a few important emacs commands that make programming in Lisp much easier (before trying these out, read the next statement on editing your '.emacs' file):

M-x run-lisp This starts Lisp as a sub-process in it's own buffer.  (Hitting enter in this buffer sends the last line to the Lisp process.  Output from the Lisp process is appended to the file)  This is good because you can simply save the buffer as a file and edit it to hand in your results (instead of cutting and pasting from the command line, which may not have a large enough buffer to hold your output anyway).
M-x lisp-eval-defun This copies the "defun" statement closest to your cursor into the Lisp sub-process.  This is good because each time you edit a function you can quickly load it into your Lisp environment (instead of saving the file and loading the entire thing into your Lisp environment)
M-x lisp-eval-defun-and-go This does the same thing as M-x lisp-eval-defun but it also automatically changes you to the buffer with your Lisp sub-process
M-p When in the buffer connected to the Lisp process, this scrolls back to the previous commands you entered.

To make the 'run-lisp' command work, you need to define the variable 'inferior-lisp-program'.  We might also want to define commands that will allow us to evaluate functions without typing so many letters each time.  To do these things, we need to edit our ".emacs" file.  Copy the following lines into your .emacs file.

(setq inferior-lisp-program "/usr/sww/lang/acl/alisp")
(global-set-key "\C-x\C-l" `run-lisp)
(global-set-key "\C-xd" `lisp-eval-defun)
(global-set-key "\C-x\C-d" `lisp-eval-defun-and-go) 


As you can see these are Lisp commands.  Emacs is written in Lisp and, when it starts, it reads and executes all the lines in the .emacs file.  The first line defines the program to execute when you type "M-x run-lisp".  The next three lines define C-x C-l to run Lisp in a new buffer, C-x d to load the current "defun" statement, and C-x C-d to load the current "defun" statement and enter the Lisp buffer. 

Getting Your Hands Dirty

This is a step-by-step process to make sure you are comfortable with the cs188 Lisp environment.  Try doing each of the following steps on your own.  If you have trouble, come to the workshop for help.

Step 1: Log into your account.  Download the AIMA code into an "aima" directory as described here. Assuming you download (or copy over) a code-2e-188.ZIP file into your home directory, you should then type "unzip code-2e-188.ZIP". Then, cd into code-2e-188 and type 'pwd'.  This is the path you should put in your aima.lisp file to define *aima-root*, where it says "Edit this". [Note: the pathname in aima.lisp must end with "/".] [Note also: it is also possible to run the AIMA code directly from the ~cs188/code-2e-188 directory without making your own copy.]

Step 2:  Download the dumb solution for q1 in assignment 0 into an "a0" directory

Step 3: edit your .emacs file as described above, or just download this one into your home directory.  Open the a0-dumb-solution1.lisp file in emacs: "emacs a0/a0-dumb-solution1.lisp"

Step 4: Type "C-x C-l" ('l' as in lisp, not '1' as in one).  This should create a new buffer which is connected to a Lisp process.

Step 5:  Type "C-x b" (and hit enter to accept the default).  This will take you back to the previous buffer (a0-dumb-solution1.lisp).  Position the cursor within the first function definition and type "C-x C-d".  This should load that function and take you back to the Lisp buffer, where you should see the output from having loaded that function.  Now try testing the function to make sure that it was loaded properly.  Type "(two-to-the 4)" and you should see '16' be returned.

Step 6: Type "M-p" and hit enter.  This will recall your last command and run it again.  You should get '16' again. (Note:  if you are running Lisp in Emacs, you may be able to press Ctrl and the up arrow to recall the previous command.)

Step 7:  Type "C-x b" to edit "a0-dumb-solution1.lisp" again.  Change the definition of 'two-to-the' by adding as the first line "(format t "n = ~s~%" n)".  Type "C-x C-d" to reload the definition and go back to Lisp. Now type "M-p" and see that we can now see the value of 'n' in recursive iterations of 'two-to-the'

Step 8:  Type "(trace two-to-the)".  This tags the function as one that we want to watch.  Now type "M-p" twice to run "(two-to-the 4)" again and see that we see each time the function is called.  "0: (two-to-the 4)" means the 0th function on the stack is "two-to-the" and it was passed the parameter "4".  We also see each time the function returns.  "4: returned 1" means that the 4th function on the stack returned with a value "1".  Since this is a recursive function, we get a nested structure. 

Step 9: Type (untrace).  This will stop all functions from being traced.  (untrace two-to-the) would only untrace that one function.

Step 10:  Replace the format expression from before with a form '(break)' and reload the function. Run it.  At the break call type ':continue 0' to continue past the break point.  When you hit the next, repeat (Ctrl+up in Emacs).  When you hit the next, this time type ':zoom' or ':up' to see the stack trace.

Step 11:  Now type     "(load "../aima/code-2e-188/aima.lisp")" "(aima-load 'agents)" "(test 'agents)".  (Note: these won't work if you are working in your own directory and didn't change the path in aima.lisp as described in step 1.  If you're working in the ~cs188/code-2e-188 directory, the first command will be "(load "~cs188/code-2e-188/aima.lisp")".)  You should see tons of output fly by on the screen.  Your version of the aima code is working if at the end you see '0'.  (Notice that you ran emacs from your home directory, but you had to specify the "aima.lisp" file as if you were in the "a0" directory because you started Lisp while editing "a0-dumb-solution1.lisp".)

Step 12:  Type "C-x s".  This will save your buffer as a file.  To turn in the output of your code, simply edit this file and comment it.

Step 13:  Edit your .newsrc file as described above and open emacs.  Type 'M-x gnus'.  Select our class newsgroup and see the messages.  Type 'a' to create a new post or 'f' to respond to a post.  Type 'C-c C-c' to send the post.


For help learning Lisp:

For the full Common Lisp reference (not a tutorial, just the facts):

For debugging suggestions:

For emacs help:

For GNUS help: