Laboratory Exercises For Computer Science 261

Beginning LISP

Beginning LISP

Goals: This laboratory exercise introduces several basic elements of the LISP programming langauge, including numbers, symbols, and functions.

Notes on the History of LISP: LISP was invented in the late 1950's and early 1960's at M.I.T. by John McCarthy. LISP is based in part on the Lambda Calculus, a mathematical formalism developed by Alonzo Church (1903-1995).

LISP is the second-oldest language still in widespread use (the oldest is Fortran). Two dialects of LISP are widely used today: Scheme and Common LISP. Scheme is a small, uniform dialect that is good for teaching because of its simplicity. Common LISP is a large, ``industrial-strength'' dialect that is standardized and is available in several commercial versions. In this course, we will use an implementation of LISP called Allegro Common LISP .

Most programming languages require learning the syntax of many different kinds of statements. In contrast, LISP syntax is simple and uniform. Much of the work in learning LISP is learning the names and effects of the system functions that form the core of the language.

Running Allegro Common LISP

Preparation: Read sections 2.1-2.4 of the textbook.

To run the LISP system, type acl <Enter> into a dtterm window. Allegro Common LISP will print out a header and then issue its own prompt [USER(1):], indicating that it is ready to examine and process any LISP program that you submit to it:

Allegro CL 4.3 [HP Prism; R1] (4/5/96 19:17)
Copyright (C) 1985-1996, Franz Inc., Berkeley, CA, USA.  All Rights Reserved.
;; Optimization settings: safety 1, space 1, speed 1, debug 2.
;; For a complete description of all compiler switches given the
;; current optimization settings evaluate (EXPLAIN-COMPILER-SETTINGS).
USER(1): 

To shut down LISP, press <Ctrl/d> at the LISP prompt, and confirm you really wish to stop. Allegro Common LISP quits after printing an exit message. Alternatively, you could type (exit) at the LISP prompt.


Elements of LISP

  1. In LISP, processing proceeds by typing expressions into the LISP environment. After each expression is entered, LISP This read-eval-print cycle forms the basis for all processing within LISP.

    Numbers are expressions, whose value or meaning is the number itself.
    Type several numbers into Allegro Common LISP, typing one number at a time. For example, at the LISP prompt, type

        7
        -10
        3.1415926535
    
    In each case, note how Allegro Common LISP responds.

  2. Accuracy: Enter a read number with many digits. How many digits are printed in the answer for integers? For reals?

  3. Fractions: Determine what happens when you enter a fraction. For example, what value is returned when you enter:
        3/5
        -18/19
        10/34
    
    How many digits are printed in the answer for rational numbers?

  4. Numeric Procedures: In LISP, we apply operations to data using a prefix notation with parentheses. For example, the function f(x) would be written (f x). Also, all operation names are listed as the first component within the parentheses. Observe what happens when you type the following operations into LISP:
        (- 27 3)
        (/ 17 2)
        (/ 17 -2)
        (/ 17 2.0)
        (truncate (/ 17 4))
        (round (/ 17 4))
        (mod 17 3)
        (sqrt 4)
        (sin 0.5)
        (sqrt -2)
        (gcd 12 15)
        (gcd 9 10)
        (+ (* 3 2) (/ 8 4))
    

  5. Symbols are LISP's analog of words; symbols are sequences of characters, such as
    hi TwoBeOrNot2Be This-is-a-symbol.
    In LISP, symbols may act as variable names, but they do not have a value until we give them one. Describe what happens when you type one of the above symbols into LISP.

  6. We may give a symbol a value using a setf operation. For example, give the symbol root2 the value 1.4142135 as follows:
         (setf root2 1.4142135)
    
    Now enter root2 into LISP and determine what happens.

  7. Check if work with symbols within Allegro Common LISP is case sensitive. What happens if you type Root2 or ROOT2.

  8. Check that the symbol pi already has the value 3.141592 by entering pi into LISP and determine what happens.

  9. Check whether LISP allows you to redefine the value of a symbol. For example, try to redefine root2 with the value -4.

  10. Next, following the 1897 Indiana House, try to declare that the value of pi is three:
         (setf pi 3)
    
    Explain what happens.

  11. Determine what happens if you make a typographical error. For example, try misspelling setf or leaving out a left or right parentheses. In each case, make a note describing what happens.

  12. Binding: The setf operation binds a symbol to a value, based on the current values of its operands. For example, consider the following sequence of definitions to compute the solutions of the quadratic equation ax¹ + bx + c = 0:
       (setf a 4)
       (setf b 5)
       (setf c 1)
       (setf discriminant 
             (- (* b b) (* 4 (* a c))))
       (setf root1
             (/ (+ (- b) (sqrt discriminant))
                (* 2 a)))
       (setf root2
             (/ (- (- b) (sqrt discriminant))
                (* 2 a)))
    
    Determine the values for a, b, c, discriminant, root1, and root2 .

    Now, redefine the values of a, b and c . Do the values of root1 and root2 change?

  13. Multiplication * as an n-ary operation: In the definition of discriminant, the multiplication operator * is used as a binary operator -- applying only to 2 operands. LISP, however, defines the multiplication operator * as applying to as many operands as desired. For example, the above expression could be written:
       (setf discriminant 
             (- (* b b) (* 4 a c)))
    
    Check the values LISP returns for each of the following expressions:
       (* 2)
       (* 2 2)
       (* 2 2 2)
       (* 2 2 2 2)
       (* 2 2 2 2 2)
    
    What happens if you do not supply any operands?
       (*)
    
    Hypothesize why you get this result.

  14. LISP includes several comparison operators:
       =      all the same
       /=     all different
       <      monotonically increasing
       >      monotonically decreasing
       <=     monotonically nondecreasing
       >=     monotonically nonincreasing
    
    The result of a comparison will be true (T) or false (NIL).

    Try these operations on several sets of numbers including the following:

       (= 1 1.0)
       (= 1 2/2 (+ (/ 1 2) 0.5))
       (< 2 4 5 8 10 12)
       (< 2 4 8/2 5 10 12)
       (<= 2 4 8/2 5 10 12)
    
    Be sure you can explain each of these results. What happens if you try the following:
       (= 1)
       (=)
    

  15. The Quote Procedure: Sometimes we want LISP to print the symbol, not its value (e.g., pi, not 3.141592). This is done with the quote procedure. To try this out, type
        (quote pi)
    
    (quote pi) may be abbreviated 'pi . Try typing this at the keyboard as well.

  16. Lambda Expressions: A lambda expression is a way of defining a mathematical function in LISP. For example, the squaring function could be written:
        (lambda (x) (* x x))
    
    This expression indicates that the input parameter is x and the result of the function will be computed as (* x x).

    Apply this function to the values 1, 3, -2 by typing:

        ((lambda (x) (* x x)) 1)
        ((lambda (x) (* x x)) 3)
        ((lambda (x) (* x x))-2)
    
    What happens if you try to apply the function to all three values at once?
        ((lambda (x) (* x x)) 1 3 -2)
    
    In LISP, a lambda expression is called a procedure.

  17. Naming Procedures: As the previous example shows, lambda expressions operate on the expression that follows them. However, in the example, the lambda expression was defined only for a single expression, and we had to type the lambda expression each time we used it. Since this is inconvenient, we often want to give the procedure a name, which we then can use repeatedly. This is done with defun statement. For simplicity in notation, defun does not use the keyword lambda. If we call the above function f, then the above computations could be done as follows:
        (defun f (x) 
             (* x x))
        (f 1)
        (f 3)
        (f -2)
    

  18. Write a LISP procedure (add2 a) that returns the sum a+2.

  19. Define a procedure f(x) = x² - 3x + 2 and evaluate it for x = 0, 1, 2, 3, 4 .

  20. Textbook Examples: If you have time, try running some programs from section 2.4 of the textbook.

This document is available on the World Wide Web as

http://www.math.grin.edu/~walker/courses/261/lab-beginning-LISP.html

created January 20, 1998
last revised January 20, 1998