[Santa Clara University]
Department of Mathematics
and Computer Science
[Return to Math 169 Homepage]

Math 169 Notes -- LISP


Contents


A. "Hello World!" in LISP

The well-known sample program in LISP can be written as follows:
          (print '(Hello World!))

B. History

LISP is an acronymn for LISt Processing Language.

LISP was developed by John McCarthy during 1958-59 and published and defined in the article: J. McCarthy, "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I," Communications of the ACM, v. 3, n. 4 (April 1960), pp 184--95.

An overview of LISP appeared in another article in the MAA Monthly: Wand, "What is LISP?" v. 91, n. 1 (Jan 1984), pp 32-42.

LISP is the classical example of a Functional programming language. "Statement" are functions that return values and can be used as argument to other functions. Because of the intrinsic functional nature of LISP, recursion is a more natural way to code functions, although iteration is also possible.

Humorously, some people suggest that LISP is actually an acronym for "Lots of Idiotic Senseless Parentheses."

C. Sample Program

NOTE: 2006. The LISP interpreter on the SCU Math-CS Dept Dell Server is GNU Common Lisp, which is invoked by the letters gcl.

One feature of many implementations of LISP is extended precision integer arithmetic.

Script started on Wed Mar 15 13:30:48 2000
math 21: more fact.lsp
(defun fact (x)
  (prog (temp)
        (setq temp 1)
        loop    (cond ((zerop x) (return temp))
                      ( T (setq temp (* temp x))
                          (setq x (1- x)) (go loop))
                      )
        )
  )

math 22: lisp
AKCL (Austin Kyoto Common Lisp)  Version(1.619) Thu Oct  7 15:46:49 PDT 1993
Contains Enhancements by W. Schelter

>(load "fact.lsp")
Loading fact.lsp
Finished loading fact.lsp
T

>(fact 100)
9332621544394415268169923885626670049071596826438162146859296389521759999322
9915608941463976156518286253697920827223758251185210916864000000000000000000
000000

>(bye)
Bye.
math 23: exit
exit
script done on Wed Mar 15 13:31:48 2000

D. Overview

(For overview information regarding I/O to files, see section L below.)

E. Data

LISP is about lists. Thus, data in LISP and their structures reflect this background to the language.
  1. Structures:
    ATOM and S-EXPRESSION

  2. Graphic Representation
    S-expressions are stored as linked binary trees. Each set of parentheses (and the dot) corresponds to an internal node and each atom corresponds to a leaf.
    	      |
    	      |
     	 _____V_____		<==>      (A . B)
    	 |    |	   |
    	 |/___|___\|			where A and B are
        ____/	   \____		"atoms."
        |A |            | B|
        |__|            |__|
    
    
    	      |
    	      |
     	 _____V_____		<==>      (A . (B . C))
    	 |    |	   |
    	 |/___|___\|	
        ____/	   \______
        |A |            |  |  |
        |__|            |/_|_\|
                     __ /     \___
                    |B |       | C|
    		|__|       |__|
    
    
                                                    (((A . B) . C) . (D . E))
    
    
    
    
    
    
    
    
  3. Components of S-Expressions:
    (In many LISP interpreters, commands may be entered either in UPPERCASE or lowercase characters.)

  4. Lists:
    E.g.,
    	      |
    	      |
     	 _____V_____		<==>      (A . NIL)
    	 |    |	   |
    	 |/___|___\|
        ____/	   \
        |A |           NIL
        |__|            
    
    
    	      |
    	      |
     	 _____V_____		<==>      (A . (B . NIL))
    	 |    |	   |
    	 |/___|___\|	
        ____/	   \______
        |A |            |  |  |
        |__|            |/_|_\|
                     __ /     \
                    |B |      NIL
    		|__|
    
    
    
    	      |
    	      |
     	 _____V_____		<==>  ((A . (B . NIL)) . (C . NIL))
    	 |    |	   |
    	 |/___|___\|	
        ____/	   \______
        | | |           |  |  |
        |_|_|           |/_|_\|
        /   \___       /      \
       A    | | |     C       NIL 
    	|_|_|
            /   \
           B    NIL
    

  5. Alternative List Notation:
  6. Variables:
    Any identifier (alphanumeric string beginning with a letter) can be assigned any value (numeric, atomic, s-expression). Identifiers do not have to be declared before use. Since all identifiers are fundamentally pointers, they can change their "type" while being used.

F. Constants

  1. Numeric:
    Integers are numbers written without a fractional part (they may or may not include the base point). Bases other than 10 are permitted, with digits preceded by # and a code, e.g., #b1001 is binary (=9 base 10), #o11 is octal (=9 base 10) and #xb is hexadecimal (=11 base 10).

    LISP integers (for practical purposes) have no maximum size. (This features is not true in some PC-freeware versions, e.g., XLISP.) Since LISP is based on lists, large integers can be implements as a list of smaller integers. Appropriate arithmetic is automatic.

    Ratios are rational numbers written as a fraction of integers. E.g., 7/3 is a ratio.

    Reals are written with a decimal point and at least one digit to the right of the decimal point. E.g., 1. is an integer, but 1.0 is a real number.

    Complex numbers are written as a list of two elements preceded by #c. E.g., #c(2 3) is the complex number 2+3i.

  2. Character:
    Non-numeric atoms are stored as character strings. To prevent attempted evaluation, such strings are preceded by a single quote mark.

  3. Lists:
    A variable can be assigned a list or s-expression as a "constant" value as well. The empty list, (), is the constant NIL.

  4. Predicate/Boolean/Logical:
    T is the true constant. NIL is the false equivalent.

  5. Other:
    See a reference book for other elements, such as arrays, vectors, strings.

G. Operators

There are no binary operators in LISP. Since all arithmetic operations are "transformed" into functions, binary operators are replaced by functions instead.

H. Environment Note

LISP is interactive! As soon as any expression is enterred, the interpreter attempts to evaluate it and a value (numeric, T, nil, or other) or an error message is returned.

I. Functions

  1. Basic Functions:
    quote
    or '
    • one argument (s-expression)
    • value is the argument UNevaluated
    E.g., (quote (add sugar to tea)) results in (add sugar to tea)
    or '(a b c) results in (a b c).
    car
    • one argument (non-atomic s-expression)
    • value is the left part of the root dotted pair
    E.g., (car '(a . b)) results in a
    Note: If the argument is a valid list, the return value is equivalent to first element of the list as an element.
    E.g., (car '(a b c)) results in a
    and (car '((a b) (c d)) results in (a b).
    cdr
    • one argument (non-atomic s-expression)
    • value is the right part of the root dotted pair
    E.g., (cdr '(a . b)) results in b
    Note: If the argument is a valid list, the return value is equivalent to list with the first element omitted (as a list).
    E.g., (cdr '(a b c)) results in (b c)
    and (cdr '((a b) (c d)) results in ((c d)).
    Pictorially, we have the following:
                        ___      ___      ___
    (A B C)  <==>   -->[_|_]--->[_ _]--->[_|_]--->NIL
                        |        |        |
    		    A        B        C
    
    (car '(A B C)) corresponds to the left portion of the root node which is merely the atom A.

    (cdr '(A B C)) corresponds to the right portion of the root node which is

                  ___      ___
             --->[_|_]--->[_|_]--->NIL
                  |        |
    	      B	       C
    
    which is the list (B C).
    cons
    "construct"
    • two arguments (s-expressions)
    • value is a new dotted pair with the first argument as the CAR and the second as the CDR.
    E.g., (cons 1 2) results in (1 . 2)
    (cons 'A '(B C)) results in (A B C)
    (cons 'A NIL) results in (A).
    setq
    • two arguments (first is a literal atom, second is any s-expressions)
    • value is the value of the second argument
    • side-effect: the first argument takes on the value of the second argument as its "value" (association).
    E.g., (setq a '(1 2 3)) means that a has the value of the list (1 2 3).
    Thus, (car a) results in 1 and (cdr a) results in (2 3).
    NOTE: There are standard abbreviations for the repeated composition of CAR and CDR. Each abbreviation begins with C and ends with R and includes a string of (up to 4) A's and D's within. Thus,
    (cadr x) is equivalent to (car (cdr x))
    (cdar x) is is equivalent to (cdr (car x))
    (cadar x) is is equivalent to (car (cdr (car x)))
    etc.
  2. Predicate Functions:

    ELEMENTARY PREDICATES:
    atom
    • one argument (s-expression)
    • value is T if argument is an atom
      NIL otherwise.
    E.g., (atom 'ant) results in T
    (atom '(A B C)) results in NIL.
    NULL
    • one argument (s-expression)
    • value is T if argument is NIL
      NIL otherwise.
    E.g., (NULL NIL) results in T
    (NULL '()) results in T
    (NULL '(A B C)) results in NIL.
    NOTE: This function can be used to detect the end of a list.
    EQ
    • two arguments (s-expressions)
    • value is T if both arguments are the same object (atom) (i.e., point to the same structure)
      NIL otherwise.
    E.g., (EQ 'A 'A) results in T
    (EQ 1 '(1 2)) results in NIL
    NOTE that the next function is similar, but is non-elementary.
    EQUAL
    • two arguments (s-expressions)
    • value is T if both argument have the same structure (i.e., have the same appearance when printed out)
      NIL otherwise.
    E.g., (EQUAL 'A 'A) results in T
    (EQUAL '(A) '(A)) results in T
    BUT (EQ '(A) '(A)) results in NIL.

    NUMERIC PREDICATES:
    numberp
    • one argument (s-expression)
    • value is T if argument is a numeric atom
      NIL otherwise.
    oddp
    • one argument (s-expression)
    • value is T if argument is an odd integer
      NIL otherwise.
    zerop
    • one argument (s-expression)
    • value is T if argument is zero
      NIL otherwise.
    plusp
    • one argument (s-expression)
    • value is T if argument is strictly positive
      NIL otherwise.

    MISCELLANEOUS PREDICATES:
    member
    • two arguments (first is an s-expression, second is a list)
    • value is T if first argument is a list element of the second argument (i.e., not an element of an element)
      NIL otherwise.
    E.g., (member 1 '(2 1 3)) returns the value of T
    (member 1 '(2 (1 2) 3)) return the value of NIL.

  3. Arithmetic Functions:
    (+ x y)	returns x+y		[OTHER LISPs may use PLUS]
    (* x y)	returns x*y		[OTHER LISPs may use TIMES]
    (- x y)	returns x-y		[OTHER LISPs may use DIFFERENCE]
    (/ x y)	returns x/y		[OTHER LISPs may use QUOTIENT]
    
    NOTE: With Common LISP, if both x and y are integers, the result from a division is given as an integer or as an integer ratio. To convert the value to a real number, one needs to multiply it by 1.0 or use real numbers as arguments.

    Each of these four arithmetic functions admit of more than two arguments. In this case, the operator is applied successively after evaluating the arguments from left to right.

    (1+ x)	returns x+1		[OTHER LISPs may use ADD1]
    (1- x)	returns x-1		[OTHER LISPs may use SUB1]
    
    A sample translation of C/C++ code to LISP:
            B = 2;
    	C = 3;
    	A = 3*(B+C);
    
    translates into
           (setq b 2)
           (setq c 3)
           (setq a (* 3 (+ b c)))
    
    Common LISP also has two functions for "remaindering" after division, rem and mod. Both return the same answer when the result of a division is a positive number, but when the result is negative, the results are different. See a reference book for the technicalities.

  4. List Functions:
    (LIST a b c ... d) returns the list (a b c ... d)

    (LAST list) returns last dotted pair of list

    (LENGTH list) returns the length of list

    (NCONC list1 list2) concatenates list1 and list2 together into one list, destroying the old list1 and list2 as independent lists. List1 has been changed and list2 is part of it. This basically takes the rightmost NIL in list1 and replaces it by a pointer to list2.

    (APPEND list1 list2) creates a new list which is the concatenation of list1 and list2 without changing list1 or list2.

    NOTE: The visible output of NCONC and APPEND with the same arguments would be the same. But the functions work in significantly different ways.

    (REVERSE list) creates a new list which is the reverse of list.

    (NREVERSE list) reverses pointers in old list (on the "top" level). The old list has been changed.

    (RPLACA list y) replaces (CAR list) with y.

    (RPLACD list y) replaces (CDR list) with y.

    NOTE: These are "non-elementary" fucntions. It is possible to construct these functions as combinations of elementary functions (and the use of recursion).

  5. Other Functions:
    COMPARISON PREDICATES:
    (> x y)         returns T if number x is greater than number y
    
    (>= x y)        returns T if number x is greater than or equal to
            	number y
    
    (< x y)         returns T if number x is less than number y
    
    (<= x y)        returns T if number x is less than or equal to
            	number y
    
    (= x y)		return T if numbers x and y are equal
    
    MATHEMATICAL FUNCTIONS:
    Note that pi is a predefined value.
    (expt base exponent)   returns the value of the base raised
                    to the power of the exponent
    
    Common LISP includes the standard mathematical functions taking a single argument, such as sqrt, gcd, lcm, exp, abs, sin, cos, tan, asin, sinh, asinh and others.

  6. Special Function--EVAL:
    eval
    • one argument (list)
    • value is the value of the argument as if it were a function expression.
    E.g.,
         (eval '(+ 1 2))
                         results in 3
         (setq a '(b 1 2))
         (setq a (cons '+ (cdr a)))
         (eval a)
                         results in 3
    
    NOTE: One can image the LISP system itself as being the function EVAL.

    NOTE: This function is central to the power of LISP. By means of this function, DATA becomes PROGRAM and thus, a PROGRAM can modify itself and thus, in some sense, "learn."

  7. Choice Function:
    The choice function in LISP is called the CONDITIONAL (abbreviated COND) and combines the functions of a C/C++ switch statement with a generalized IF...THEN...ELSE(IF) statement.

    COND takes several two-element lists as arguments.
    The first element in each of these lists is a conditional expression and the other element is an action. E.g.,

    (COND (condition1 action1)
          (condition2 action2)
          (condition3 action3)
          ...
          (condition-m action-m) )
    
    The first condition that evaluates to T has its corresponding action performed--all others are ignored. For example,
        (setq a 1)
        (cond ((zerop a) (setq b 2))
              ((oddp a)  (setq b 5))
    	  (t         (setq b 7)))
    
    What is the value of b?

  8. User-Defined Functions:
    A user-defined LISP function consists of several components:
    1. the LISP function name which indicates the defining of a user function;
    2. the literal atom which is the name of the user function;
    3. the "lambda" expression (related to the l [lambda] calculus) which, in Common LISP, consists of (1) list of arguments to the function, and (2) the body of the user function.

    (Older versions of LISP included the word "LAMBDA" as well, a nod to the origins of LISP.)

    In general, a function definition takes the form:

          (DEFUN function_name input_var_list action)
    
    COMMON LISP allows more than one action. They are merely listed in order.

    Example 1: Function to see if EL is in list LIST.

        (defun contains (EL LIST)
    1)        (cond ((null LIST) NIL)
    2)	      (t    (cond ((equal EL (CAR LIST)) T)
    3)	                  (t (contains el (cdr LIST)))))))
    
    Think recursively!
    Use "divide and conquer" techniques.
    How do we divide a list in LISP?
    Which functions to we use to create which two parts? _______ _______

    The CAR is not a list. To test to see if EL equals the CAR (and thus whether EL is in LIST), we use EQUAL (line 2). If EQUAL returns T, we are done; if NIL (false) we continue our search.

    CDR is a list. To test to see if EL is in this section of the original LIST, we re-use CONTAINS (line 3).

    In the extreme case, LIST may be NIL (e.g., when we have run out of elements to test in a list and take the CDR, we get the null list) (line 1).

    Example 2: Function to compute the length of list LIST.

        (defun howlong (LIST)
    1)        (cond ((null LIST) 0)
    2)	        ((atom LIST) 0)
    3)		((null (cdr LIST)) 1)		
    4)		(t (1+ (howlong (cdr LIST))))))
    
    Once again we try to think recursively, and try to use "divide and conquer" techniques.

    Given a list, if the CDR is NIL, there is only one element in that list, so the length must be 1 (line 3).
    If the CDR is not NIL, i.e., if LIST is a list, we use recursion to find the length of the CDR and add 1 to that result (line 4).
    For example,

              (3  4  2  1  5)
               |  \________/
              CAR     CDR   ===> howlong is the CDR? => 4
              \____________/
                  length of complete list => 1 + 4 => 5
    
    In extreme cases, LIST may be an atom (line 2) or NIL (line 1).

  9. Sequential Flow:
    Recursion is the background ambience of LISP. It may happen, however, that (1) a programmer may not be able to conceive of a recursive implementation of some function (even though it may theoretically exist) or (2) a recursive implementation is less efficient, may use substantially more memory than a non-recursive version, and may use up memory resources in the process of numerous recursive calls.

    Because of this, LISP provides sequential flow functions that also allow internal branching (and thus provide for looping). More recent versions of LISP (including Common LISP), provide for explicit loop constructs.

    The classical LISP sequential function is PROG.
    The branch function is GO.
    The return function is RETURN.

    PROG takes several arguments. The first is a list of local variables. The other arguments are functions executed in the order they are included.
    PROG provides a means to group several actions together (such as the { ... } pair in C/C++). As in C/C++, such a "block" also can have local variables declared.
    The value of the PROG construct is the value explicitly returned by the RETURN statement if the PROG includes one and if it is is executed. Otherwise, PROG returns NIL.

    GO takes one argument, a literal atom, which acts as a label before any function within the PROG. Its value is NIL. Its side-effect is to transfer flow of the code to the function following the literal atom.

    RETURN takes one argument. Its value is the value of the argument. Its side-effects are (1) that its value becomes the value of the PROG in which it is found and (2) that the execution of the PROG structure terminates.

    PROG statements were needed in early versions of LISP for the same reason why braces are needed in C/C++, namely to group multiple statements together as one unit. PROG with a GO also was the only way to create an iterative loop in earlier versions of LISP. In most cases, Common LISP does not require a PROG to group statements. Common LISP also provides alternative means for repetition.

    Example: Sequential/iterative version of the length function howlong.

           (defun howlong2 (list)
             (prog  (temp num)
    	        (setq temp list)
    		(cond ((atom temp) (return 0))
    		      ((null temp) (return 0)))
    		(setq num 0)
    	  loop  (setq num (1+ num))
    	        (setq temp (cdr temp))
    		(cond ((null temp) (return num))
    		      ( t   (go loop)))))
    
    This approach uses a pointer temp to point to the list. Each time through the loop, the CAR is omitted, and temp is reset to the (cdr temp). At the same time, num is incremented by 1. When temp has been reset to the end of the list, NIL, num indicates the length of list.

    NOTICE that this versions is about double the length of the recursive version (in section I-8, Example 2)!

    An attempt to translate the recursive version into a C++-like language would result in code such as:

             class node
    	 {public:
    	       node * info;
    	       node * next;
    	  };
    	 int howlong(node * list)
    	 {
    	       if (list == NULL)
    	           return 0;
    	       else if (atom(list))
    	           return 0;
    	       else if (list->next == NULL)
    	           return 1;
    	       else
    	           return (1 + howlong(list->next));
             }
    
    Note that this code is problematic since it would be difficult to code a function atom to determine whether the input list was either a pointer to a list or a character atom or an integer.

    The corresponding iterative version might look like the following:

             int howlong2(node * list)
    	 {
    	      node * temp;
    	      int num;
    	      temp = list;
    	      if (atom(list))
    	          return 0;
    	      else 
    	      {
    	          num = 0;
    		  do{
    		     num++;
    		     temp = temp->next;
    		    }
    		  while(temp!=NULL);
    		  return num;
    	       }
    	  }
    
  10. Loops:
    Looping using PROG and GO
    Older versions of LISP did not have any specific looping function, since none was felt necessary, given the power of recursion and the mathematical origins of LISP. For the cases where iteration seemed preferable, programmers used the GO construct witin a PROG function. For example,
    (DEFUN FACT (X)
         (PROG (ANS)
    	     (SETQ ANS 1)
    	LOOP (COND ((ZEROP X) (RETURN ANS))
    		    (T      (SETQ ANS (* ANS X))
    			    (SETQ X (1- X))
    			    (GO LOOP))
    	      )
          )
     )
    
    But, PROG and GO are neither part of "pure" LISP and, since they can lead to "spaghetti" code as in other languages, modern programmers recommend avoiding such constructs when possible.

    To include iteration, more recent dialects (such as Common LISP) have include iteration functions.

    Generalized DO

       (DO (
             (var_1  init_val_1  step_1)
             (var_2  init_val_2  step_2)
    	 ...
             (var_n  init_val_n  step_n)   )
    
           ( end_test
                end_action_1
    	    end_action_2
    	    ...
    	    end_action_k
    	    return_value   )
           body_action_1
           body_action_2
           ...
           body_action_m
           )
    
    This DO construct is common in most dialects of MACLISP and Common LISP. It is a looping structure which eliminates the need for the PROG and GO structures of "ancient" LISP.

    The DO construct consist of three major sections:

    For example,
    (defun fact (x)
        (do (
    	 (temp x (1- temp)) ;sets temp to x initially and
    	                 ; decrements temp each time through loop
    	 (ans 1)       ;sets ans to 1 initially 
    	    ) 	       ; (used as culmulative product)
    	((zerop temp) ans)        ;exit condition and return value
    	(setq ans (* ans temp))   ;body of loop
         )
     )
    
    DOTIMES loop
       (DOTIMES 
            (loop_var  end_value  opt_return_value)
    	body_action_1
            body_action_2
            ...
            body_action_m
        )
    
    This DOTIMES construct is available in most dialects of Common LISP. It is a counted loop in which the actions indicated in the body of the loop are performed the number of times determined by the limit indicated for the loop_var. When the actions in the loop are being executed, the value of loop_var begins with 0 and goes up to but does not include end_value. As soon as loop_var reaches the value of end_value the structure is exited with the value of the structure being that of opt_return_value (which is optional).

    For example,

    (defun fact (x)
        (setq ans 1)
        (dotimes
            (i x ans)   ; i is loop var and x is upper limit
    	            ; with ans being the return variable
    	(setq ans (* (1+ i) ans))
         )
    )
    
    Generalized LOOP
    LOOP is a generic infinite loop available in most recent versions of Common LISP. To exit, one must also include an appropriate clause, such as a WHEN action with a return value.

    The WHEN construct's structure is as follows:

         (WHEN predicate_expression (RETURN return_value))
    
    The predicate_expression could be any test that evaluates to T or NIL.

    The LOOP contruct itself has this structure:

         (LOOP
           body_action_1
           body_action_2
           ...
           body_action_m
          )
    
    The LOOP executes all body_actions in the order listed repeatedly. One of the body_actions must be some sort of test (such as the WHEN) in order to break out of the loop. For example,
    (defun fact (x)
        (setq ans 1)
        (loop
    	(setq ans (* x ans))
    	(setq x (1- x))
    	(when (zerop x) (return ans))
         )
    )
    
  11. Special Functions:
    Some functions and features are system and version dependent, but very useful!

J. Parameter Passing

LISP uses two parameter passing schemes: call-by-value and call-by-name.

Call by Value: Most LISP functions and user-defined functions have parameters passed via call-by-value. The actual parameters are first evaluated, and then these values are bound to the formal parameters. None of the parameter is changed in the calling program upon completion of the function, except via the function return value.

Call by Name: In a few LISP functions (e.g., COND, QUOTE, DEFUN), the actual parameters are not evaluated but stored as a literal string. These parameters are evaluated only when needed (used).

K. "Pure" LISP and Recursion

LISP's power come from Programming recursive functions can be elegant and simple, but may require "unlearning" any tendencies to program sequentially. Sequential programming (with iterative loops) by nature involves a series of commands to change a global state rther than one command to change a local incarnation.

Some authors speak about "pure" LISP which does not use PROG (nor RETURN nor GO). It is a lisp that involves (because arithmetic functions), the primitives CAR, CDR, CONS, EQ, ATOM, QUOTE, COND, DEFUN alone.

L. I/O -- Input/Output

  1. General:
    As with some other languages, LISP I/O functions have system variations especially when dealing with files.

    The following are some of the more common commands:

    (PRINT a) prints the value of a (on the monitor). (Remember that each evaluation of a function will return the function's value to the monitor. (PRINT a) is intended for returning one or more values in the midst of a program, rather than as the final value.)

    (TERPRI) induces a carriage return (on the monitor).

  2. File I/O:
    To OPEN file FOO.LSP in your directory and prepare it as an INPUT file one uses:
         (SETQ IN1 (OPEN "FOO.LSP" :DIRECTION :INPUT))
    
    where IN1 can be any local atom. This associates the local atom IN1 with the disk file FOO.LSP. A similar process is necessary with C++ when using disk files, e.g.,
          #include<fstream.h>
          ifstream Infile;
          Infile.open("in.dat");
    
    Then to READ from file FOO.LSP, use:

    (READ IN1) reads one s-expression from the file associated with IN1 and return the s-expression as its value.

    (READ-CHAR IN1) reads one character.

    (Other functions are possible -- see a complete Common LISP manual.)

    To CLOSE the file associated with IN1:

        (CLOSE IN1)
    
    In a similarly fashion, to OPEN file BAR.LSP in your directory and prepare it as an OUTPUT file:
         (SETQ OUT1 (OPEN "BAR.LSP" :DIRECTION :OUTPUT))
    
    Then to PRINT to BAR.LSP, use:

    (PRINT s-exp OUT1) to print in a form which can later be used by READ, with a preceding NEWLINE and followed by SPACE and escape characters if necessary.

    (PRIN1 s-exp OUT1) to print in form which can later be used by READ, without any surrounding spaces but with escape characters if necessary.

    (PRINC s-exp OUT1) is like PRIN1 except special characters are printed without other marks (with NO escape characters).

    (TERPRI OUT1) forces a carriage return in file OUT1.

M. Association List

An association list (A-list) is a list of dotted pairs. The CAR of each pair is a literal atom used as a variable. The CDR of each pair is the (current, or most recent) value.

A-lists can be used in many ways, but one use is to provide a way to reference values (i.e., bindings) of variables upon recursive function calls (and returns). Since recursion is common, there needs to be a way to reference the "most recent" binding of a given variable. This is done via the association list (A-list).

New associations are usually added before the CAR of the A-list, resulting in new CAR. When a value is needed corresponding to a specific atom (i.e., variable), the list is searched starting with the CAR and at the first discovery of the desired atom, the value associated with it is used.

In any A-list, it is possible that a literal atom may appear several times. When the list is searched, however, only the instance nearest to the head of the list is identified.

N. A-List Modifications

For the internal A-list, modifications are performed as follows:
  1. Function Calls:
    on entry, formal parameters are paired with actual parameters (evaluated or not) and inserted at the front of the A-list.
    on exit, these pairs are deleted.
  2. PROGs:
    on entry, local variables are paired with NIL and inserted at the front of the A-list.
    on exit, these pairs are deleted.
  3. SETQ:
    searches the A-list for the first instance of the atom and replaces the corresponding CDR with a new value. If the variable is not found, it creates a new pair and inserts it at the front of the A-list.

O. Property List

Each atom (literal or numeric) can have an associated property list (P-List) plus a type descriptor in additional to a value. The property list contains pairs in which the CAR is the property name and the CDR is the value. (Some implementations merely have one list of elements in which the odd elements are the properties and the following even elements are the associated values.)

If the atom is a function name, its property list will contain an indicator giving the function type.

The major functions used with P-Lists are the following:

(SETF (GET x z) y) --- gives x the z-property value of y. [OTHER LISPs may use (PUTPROP x y z).] If x and z are atoms (used as names), they should be preceded by a quote mark. For example,

     (setf (get 'sam 'eyes) 'blue)
In a language such as Icon, in which "arrays" could have character strings as subscripts, this LISP function could be seen to be equivalent to sam["eyes"] = "blue".

(GET x z) --- gets the value of the z-property of x. If x and z are atoms (used as names), they should be preceeded by a quote mark. For example,

     (get 'sam 'eyes)
should return the value BLUE (assuming the earlier value setting).

(REMPROP x z) --- removes the z-property from the P-List of x.

(SYMBOL-PLIST x) --- returns the entire P-List of x (with all the values of the properties).

P. Other Lists

  1. Object List:
    The object list or oblist is the symbol table for LISP. Each literal atom is stored once in the oblist with pointers to its P-List.

  2. List of Available Space:
    The list of available space or LAVS is the "heap" from which memory locations are obtained when lists are created or expanded. When LAVS is exhausted, a garbage collection is performed.

Q. Pragmatics

LISP programs are normally interpreted.

Memory is divided into a static area and a dynamic area. The static area contains the translator (if it exists), the interpreter, I/O interface, and the garbage collector. The dynamic area contains the various lists (A-List, Oblist, LAVS, and P-Lists) along with the user program code.

R. Sample Code

S. Sample Session

Script started on Wed Apr  7 21:11:37 1999
math 21: cat foo.lsp
(defun for (a)
      (prog ()
	    loop (print (car a))
	         (setq a (cdr a))
		 (cond ((not (null a)) (go loop)))))
(defun rev (x)
      (cond ((null (cdr x)) (print (car x)))
	    ((prog () (rev (cdr x))
		      (print (car x))))))
math 22: lisp
AKCL (Austin Kyoto Common Lisp)  Version(1.619) Thu Oct  7 15:46:49 PDT 1993
Contains Enhancements by W. Schelter

>(+ 1 2)
3

>(setq a 2)
2

>(setq b '(a 2 3))
(A 2 3)

>(car b)
A

>(cdr b)
(2 3)

>(cons '+ (cdr b))
(+ 2 3)

>(setq c (cons '+ (cdr b)))
(+ 2 3)

>c
(+ 2 3)

>(eval c)
5

>(load "foo.lsp")
Loading foo.lsp
Finished loading foo.lsp
T

>(setq d '(1 2 3 4 5 6 7))
(1 2 3 4 5 6 7)

>d
(1 2 3 4 5 6 7)

>(rev d)

7 
6 
5 
4 
3 
2 
1 
NIL

>(for d)

1 
2 
3 
4 
5 
6 
7 
NIL

>(bye)
Bye.
math 24: exit
math 25: 
script done on Wed Apr  7 21:13:45 1999


This page is maintained by Dennis C. Smolarski, S.J. dsmolarski@math.scu.edu
Last updated: 29 March 2000. Minor corrections: 29 April 2002, 12 October 2005, 8 November 2007.