For next time, you should add content on the break statement, how to debug in general, and how to use the debugger in particular. Or perhaps that should be a second lab. You should also add content on the let statement.

Lisp Introductory Lab

Scattered throughout this lab are a series of questions. They are intended to provoke thought and give you practice, and you should ask for help if the answers aren't clear; but you do not need to turn in answers. At the end of the lab are two assignments that you need to turn in.

Part 0: Starting up CLISP and getting started.

  1. At a Linux prompt, type clisp and hit Enter.
  2. This should start up the CLISP environment. At the CLISP prompt, type

    (+ 3 4)

    and hit enter.

  3. This should add 3 to 4. In Lisp, + is a function (referred to as a method in Java). To make a function call in Lisp, you place the name of the function and its arguments, separated by spaces, inside parentheses. This takes a little getting used to! In Java, function calls look something like this:

    function(arg1, arg2, arg3);

    In Lisp, function calls look like this:

    (function arg1 arg2 arg3)

    If you have taken CS 217 (Programming Languages), you may have encountered Scheme (depending on the year that you took it). Scheme is similar, but not identical, to Lisp.

Part 1: Basic Lisp Primitives

  1. At the CLISP prompt, enter the following:

    (first '(apple orange pineapple))
    (rest '(apple orange pineapple))
    (first (apple orange pineapple))
    
    If you get stuck at a "break" prompt, you can type "quit" to get back to the high-level CLISP prompt.

    What do first and rest do? Why is the single quote necessary? What does it do?

  2. Write sequences of first and rest that will pick the symbol pear out of the following expressions:

    (apple orange pear grapefruit)
    (((apple) (orange) (pear) (grapefruit)))
    (apple (orange) ((pear)) (((grapefruit))))
    
  3. Execute the following statements. What do the functions cons, append, and list do?

    ; This is a comment, by the way!
    (cons 'x '(1 2))
    (cons '(1 5) '(2 3))
    (append '(1) '(2 3))
    (append '(1 5) '(2 3))
    (list '1 '2 '3 '(4 5))
    
  4. Execute the following code. What results do you get, and why?

    (setf mylist '(hey there dude))
    (first mylist)
    (setf (first mylist) 'bye)
    mylist
    
  5. Execute the following code. What do length and reverse do?

    (length '(plato socrates aristotle))
    (reverse '(plato socrates aristotle))
    
  6. Enter the following code.

    (nth 1 mylist)

    What does nth do?

Part 2: Editing code and defining functions

Clearly, you don't want to keep entering all your code into the CLISP prompt. You can write functions in a text editor, then load them into CLISP. You can use any editor you want, but you will go crazy if it isn't an editor that can handle matching parentheses. NEdit should have this enabled by default, but make sure you turn it on if it's off. I'm a big fan of the emacs editor myself, and I recommend learning it: it's really powerful, and it understands Lisp (indents your code correctly, etc.). The choice of an editor is often a personal one. I suspect Eclipse can also be made to work with clisp, but I haven't tried it.

  1. Open up your choice editor. If you want to try emacs, change to the directory where you want to work, and type

    emacs part2.lisp &

    This will open up an emacs window to edit the program part2.lisp.

  2. Type in some Lisp code:

    (setf x (+ 3 2))
  3. Save your program in a file called part2.lisp. If you're trying emacs, do so by keying ctrl-x followed by ctrl-s, or go to the menu bar and selecting Files, followed by Save Buffer.

  4. In your CLISP window, type

    (load "part2.lisp")

    This will execute whatever code is in your file. In CLISP, type

    x

    at the prompt. You should see a 5 in response.

  5. Remove the code that you currently have in part2.lisp, and instead type or copy in the following. If you're using emacs, you can hit the tab key on each line so that it will automatically indent your code for you. Save when done.

    (defun both-ends (whole-list)
      (cons (first whole-list)
            (last whole-list)))
  6. In CLISP, load in the function. Then type

    (both-ends '(breakfast lunch dinner snack pizza))

    What does the function both-ends do? How? Where is the name of the function? Where are the parameters of the function? How does it determine which value to return?

  7. Define rotate-left, a function that takes a list as its argument and returns a new list in which the former first element becomes the last.

  8. You can declare "local" variables in LISP via the use of the let function. For example, run the following code:

    (let ((a 1) (b 2))
      (setf a 7)
      (setf c (+ a b)))
    

    After executing this code, what are the values of a, b, and c? Why?

Part 3: Conditionals

Lisp has different predicates for testing equality.
  1. At the CLISP prompt, type

    (setf x '(hi there))
    (setf y '(hi there))
    (setf z 'bye)
    (equal x y)
    (equal x z)
    

    What kind of responses do you get? Why?

  2. At the CLISP prompt, type

    (equal 4 4.0)
    (= 4 4.0)
    (= x y)
    

    What kind of responses do you get?

    In short: = only works on numbers, but will return the correct answer if the types are different. equal will work on all types, but distinguishes between numbers of different types as different.

  3. Define the function divisible-by which takes two numeric arguments, and determines if the first is divisible by the second. For example, (divisible-by 10 2) should return T, while (divisible-by 10 3) should return NIL. Use the built in function rem that takes two integer arguments and returns the remainder when the first argument is divided by the second.

  4. Enter the following code:

    (setf x 5)
    (if (= x 3)
        (setf y 9)
        (setf y 10))
    

    What value is assigned to y? What's happening here? Try changing the value of x in the initial setf statement.

    The following code acheives the same purpose. Why does it work?

    (setf x 5)
    (setf y (if (= x 3) 9 10))
    
  5. Enter the following code:

    (setf x 8)
    (setf y (cond ((= x 3) (+ 3 8))
                  ((= x 8) 12)
                  (t (* 6 3))))
    

    Can you change the value of x to get different values of y? What does each portion of the cond function do?

Part 4: Recursion

  1. Enter and load the following function. What does it do? Why?
    (defun mystery (m n)
      (if (zerop n)
          1
          (* m (mystery m (- n 1)))))
    

    Note that there is no "return" statement here, as you would find in a similar function in Java. Why? How is the return value determined in the above function?

  2. Write a function, keep-first-n, that returns a list of the first n elements in a list. You may assume that there are at least n elements.

    Example:

    > (keep-first-n 3 '(a b c d e f g h i))
    (A B C)
    

Part 5: Iteration and printing

  1. Enter and load the following code. What does it do? Why?

    (dotimes (i 10)
      (format t "Count = ~a~%" i))
    

    Try removing the ~% from the format statement. What does the ~% do? What does the ~a do?

  2. Try this version:

    (dolist (i '(a b c d e))
      (format t "Symbol = ~a~%" i))
    

    How does dolist differ from dotimes?

  3. Try this version:

    (setf count 0)
    (loop
      (if (equal count 10) (return count) nil)
      (format t "Count = ~a~%" count)
      (setf count (+ count 1)))
    

    What purpose does (return count) serve? When would you use loop instead of the other options?

  4. Enter the following code:

    (setf x 8)
    (setf w
       (if (equal x 8) (progn (setf y 2)
                              (setf z 3))
                       (progn (setf y 7)
                              (setf z 9))))
    

    What purpose does progn serve? What is w equal to in the end? Why?

Exercise due Wednesday

Submit one file called num-times.lisp which contains the definitions for the two functions assigned below. Use usual good programming style: indent code appropriately, document your code where hard to read (a semicolon comments out a line of code), and include header comments for each function indicating author(s), purpose of the function, etc. Do these exercises with your partner from lab.

Create functions num-times-A-iterative and num-times-A-recursive, which are iterative and recursive definitions of a function that takes a list and returns the number of times the symbol A occurs in it. A is not a variable, or a parameter; it is a case-insensitive Lisp symbol, of which you should count how many times it appears. For example:

> (num-times-A-iterative '(2 a hello a elephant A))
3

Exercise due Friday

Create functions add-position iterative and add-position-recursive, which are iterative and recursive definitions of a function which takes a list and returns a list of each element plus its position. Positions start at 0. For example:

> (add-position-iterative '(7 5 1 4))
(7 6 3 7)

You may find the recursive version easier if you first create a helper function that takes two parameters: a list, and a starting position number.

Submit both functions in a file called add-position.lisp