The Department of Computer Science & Engineering
cse@buffalo
STUART C. SHAPIRO: Lisp Short Course

A Short Course in Common Lisp
Summer, 2004
Stuart C. Shapiro & David R. Pierce


Copyright © 2004 by Stuart C. Shapiro & David R. Pierce. All rights reserved.

Meeting time and place:
MTh 10:30 - 11:30, Baldy 21

Syllabus
  1. Common Lisp Resources

  2. Common Lisp Development Environment
    For file preparation, testing, and debugging:
    • xemacs &
    • For sample initialization files see the directory ~shapiro/.xemacs/, and, especially, ~shapiro/.xemacs/init-for-ac.el.
    For file preparation:
    1. Use a file extension of .cl
    2. Use Common Lisp mode
    3. See the Common Lisp mode commands by doing C-h m
    For testing and debugging:
    1. M-x run-acl
    2. Answer all questions by entering RET
    3. See the Inferior Common Lisp mode commands by doing C-h m

  3. Lisp Execution Style
    • Expression-oriented: Evaluate a series of expressions.
    • Optionally take input from a file, then from standard-input.
    • The Lisp Listener.
    • The read-eval-print loop.

  4. Numbers
    • A number is an atom that evaluates to itself.
    • Test read-eval-print loop
      Really:
      1. Read
      2. Construct an object
      3. Evaluate the object
      4. Choose a printed representation
      5. Print
    • Two surprises:
      1. bignums
      2. ratios

  5. Non-Atomic Expressions

  6. (X)Emacs' *scratch* buffer is in Lisp Interaction mode
    Type C-j after a Lisp form.

  7. Exiting:
    (exit) or :ex to the Lisp Listener.

  8. Booleans
    • Lisp's False is nil, an atom that evaluates to itself.
      Try it.

    • Lisp's True is t, an atom that evaluates to itself, and every other Lisp object except nil.

    • and and or are Lisp macros that take an arbitrary number of arguments, and do short-circuit evaluations.
      Try them for different numbers of arguments.
      Try them with zero arguments.
      They return t, nil, or the value of the last expression evaluated under their control, a theme in Lisp.

    Exercise: Start a file for Lisp source code.
    For now, just put some comments at the top saying what it's for.

  9. Comment Characters
    ;Rest of lineIn line after code
    ;;Entire lineIndent like code
    ;;;Entire lineStart in column 1
    #|...|#Comment bracketsUse for commenting-out code

  10. Defining Functions
    • See the ANSI Common Lisp section on the defun macro

    • Example
      (defun average (x y)
        "Returns the average of the numbers x and y."
        ;; Doesn't round or truncate integers
        (/ (+ x y) 2))
      

    • Variables have lexical scope.

    • Objects, not variables have type.
      Try (type-of n) for various numbers, n

    • Load your file:
      (load "file-name") to Listener
      or :ld file-name to Listener
      or :cl file-name to Listener
      or C-c C-b in source file buffer
      or C-u C-c C-b in source file buffer

      Then try:
      (average and then pause and look at the minibuffer
      (average 5 7)
      (average 5 8)
      (float (average 5 8))

    • Exercise: Define (discrim a b c) to return the square root of b2 - 4ac
      (discrim 2 7 5) should return 3.0

    • Surprise: Lisp functions can return multiple values
      Try (floor 5.25) and (round 5.25)

    • Example
      (defun +- (x d)
        "Returns x+d and x-d."
        (values (+ x d)
      	  (- x d)))
      

      Try: (values)

    • Exercise: Using discrim, define (quad-roots a b c) to return the two roots of the quadradic equation
      ax2 + bx + c = 0
      namely, (-b + sqrt(b2 - 4ac))/2a and (-b - sqrt(b2 - 4ac))/2a

      (quad-roots 2 7 5) should return -1.0 and -2.5

  11. Two-Branched Conditional
    (if test-form then-form [else-form])

    Note: if is a special form

    Example:

    (defun fact (n)
      "Returns the factorial of n."
      (if (<= n 0)
          1
        (* n (fact (1- n)))))
    

    Exercise: Define (fibonacci n) to return the nth Fibonacci number: 1 1 2 3 5 8 13 ...

  12. Tracing
    (trace function-name ... function-name) turns on tracing of the named functions.
    (trace) returns a list of the functions being traced.
    (untrace function-name ... function-name) turns off tracing of the named functions.
    (untrace) turns off all tracing.

    Typing C-c t when your cursor is over a function name, either in Common Lisp mode or in Inferior Common Lisp mode, toggles tracing of that function.

    ACL has a ACL trace facility with more controls.

    Try tracing discrim and quad-root.
    Try tracing fact and/or fibonacci.
    Try these with the functions both interpreted and compiled.

  13. Characters

    • Characters, like numbers, are "self-evaluating atoms". Their syntax is #\<name of character>. Try these:

      #\a
      #\space
      #\newline
      
    • Lisp handles Unicode characters and character names.

      #\latin_small_letter_a
      #\latin_small_letter_a_with_acute
      #\latin_small_letter_eth
      #\greek_capital_letter_sigma
      

      Note that #\latin_small_letter_a is another example of Lisp choosing a print syntax.

    • Now try this with each of the characters above:

      (format t "~a" #\latin_small_letter_a_with_acute)
      

      Format is the Lisp equivalent of printf, only (MUCH!) more powerful, of course. We will talk about format in detail later, but for now, format t prints to standard output, and ~a is the control sequence for printing in "human readable" format.

      Lisp can represent Unicode characters, but Emacs will only gracefull display the ones in the Latin-1 encoding (á and ð). You can try others (e.g., #\greek_capital_letter_sigma) but Emacs won't handle them as gracefully. However, you can see the Unicode using char-code:

      (char-code #\greek_capital_letter_sigma)
      
    • For character comparison, use char=, char<, char>.

    • See §13.1.1 for additional useful functions.

  14. Strings

    • Strings are also self-evaluating atoms, denoted by a series of characters between double quotes.

    • Constructing strings:

      "my string"
      (char "my string" 0)
      (char "my string" 2)
      "string with \" in it"
      (char "string with \" in it" 11) 
      (char "string with \" in it" 12) 
      (char "string with \" in it" 13)
      (format t "~a" "string with \" in it")
      (string #\latin_small_letter_a_with_acute)
      (string-capitalize "david.r.pierce")
      (string-trim "as" "sassafras")
      	      
    • Comparing strings:

      (string= "david pierce" "David Pierce")
      (string-equal "david pierce" "David Pierce")
      (string< "David Pierce" "Stu Shapiro")
      (string/= "foobar" "foofoo")
      	      
    • Strings as sequences:

      (length "my string")
      (length "\\")
      (format t "~a" "\\")
      (subseq "my string" 3)
      (subseq "my string" 3 6)
      (position #\space "my string")
      (position #\i "David Pierce")
      (position #\i "David Pierce" :start 5)
      (search "pi" "david pierce and stu shapiro")
      (search "pi" "david pierce and stu shapiro" :start2 10)
      (concatenate 'string "foo" "bar")
      (concatenate 'string
        "d" (string #\latin_small_letter_a_with_grave)
        "v" (string #\latin_small_letter_i_with_acute)
        "d")
      

      Sequences comprise several types that act as ordered containers of elements (including lists and arrays as well as strings). Each of these functions accepts sequence arguments. We'll talk about sequences more later.

    • Exercise: Define (string-1+ s) to construct a new string by adding 1 to the character code of each character in its argument. For example, (string-1+ "a b c") => "b!c!d".

  15. Symbols
    • A symbol is an atom that might have a value, but might not.
    • Syntax: almost any sequence of characters (mixed case) that can't be interpreted as a number.
      (Warning: In some older implementations, the Lisp reader upper-cases all characters entered unless they're escaped.)
    • Escape character: \
    • Escape brackets: | ... |
    • Attributes of a symbol
      1. symbol-name
      2. symbol-value
      3. symbol-function
      4. symbol-package
      5. symbol-plist
    • Quote: 'expression always evaluates to expression rather than the value of expression

    • Load your source file with the definition of average
      Try:
      (type-of 'average)
      (symbol-name 'average)
      (type-of (symbol-name 'average))
      (symbol-function 'average)
      #'average
      (type-of #'average)
      (type-of (type-of #'average))
      (function-lambda-expression #'average)
      

      Load a compiled version of the file (e.g. with C-c C-b)
      Try:

      #'average
      (function-lambda-expression #'average)
      

    • Put your cursor in your *common-lisp* buffer, and type C-x 1 to expand it.
      Move your cursor over the symbol average in your *common-lisp* buffer, and type C-c . (Really type the dot.)

    • Equality function for symbols, and identity in general: eql
      Try it.

    • How does the Lisp reader locate the symbol from what you type?
      1. Read the characters you typed, and construct a string (the symbol's name).
      2. Look up the atom from it's name in a "catalogue" (probably a hash table).
      3. If it's not there, create it, and put it there.

      The process of installing a symbol in a catalogue is called interning it,
      and a symbol that's been so installed is called an interned symbol.

  16. Packages

    A package is a catalogue (map) of symbol name => symbol. I.e., a "name space".
    There's always a current package which the Lisp reader uses to look up symbol names.
    Try typing *package* to the Lisp Listener.

    Lisp packages have nothing to do with file directories or files, though the usual style is to have a file "in" only one package.

    A symbol that's interned in a package may be internal or external in that package, and that package is considered the symbol's home package.
    Find the home package of a symbol by evaluating (symbol-package symbol)
    Try (symbol-package 'average) and (symbol-package 'length)

    Every package has a name, and may have one or more nicknames.
    Try: (package-name (symbol-package 'average))
    and (package-nicknames (symbol-package 'average))

    Mappings between packages and their (nick)names:

    (find-package package-name-or-symbol)
    (package-name package)
    (package-nicknames package)

    Evaluate (describe 'average) You should be able to understand all or most of what's printed.

    Evaluate (describe 'length) Notice how many packages there are.

    Put your cursor on a symbol in either the Lisp listener, or in a file of Lisp source code, and type C-c d, then RET in the minibuffer.

    Try (documentation 'average 'function)

    Symbol Completion: C-c TAB

    You can make a symbol external in its home package by exporting it.
    Try (export 'average)
    Describe average again.

    You can change packages in the Lisp Listener by entering :pa name-or-symbol
    Try :pa lisp Note the prompt.

    You can refer to a symbol whose home package is p from some other package whether or not it is external in p.
    To refer to an external symbol s in package p type p:s
    To refer to an internal symbol s in package p type p::s

    Try:

    'cl-user::discrim
    'cl-user::average
    'cl-user:average
    'cl-user::length
    'discrim
    
    Notice the printed representation Lisp chooses for these symbols.
    Notice that the last entry caused Lisp to create a symbol named "discrim" in the common-lisp package.

    Enter :pa user to return to the common-lisp-user package.

    Try:

    'cl-user::discrim
    'cl::discrim
    (symbol-name 'discrim)
    (symbol-name 'cl::discrim)
    (string= (symbol-name 'discrim) (symbol-name 'cl::discrim))
    (eql 'discrim 'cl::discrim)
    
    It should not be confusing that discrim and cl::discrim are two different symbols that have the same name.

    Two Special Packages

    1. The Keyword Package

      Every symbol in the keyword package is external and evaluates to itself.
      It is entered with an empty package name and a single :
      Try (describe :foo)

    2. The Non-Package

      If the reader reads #:s, it will create an ininterned symbol named "s", i.e., one that is not interned in any package.
      An uninterned symbol can never by found by the Lisp reader, and so will never be eql to any separately read symbol, even one with the same name.

      Try:

      (describe '#:foo)
      (eql '#:foo '#:foo)
      (string=  (symbol-name '#:foo) (symbol-name '#:foo))
      
      Evaluate (gensym). gensym creates new uninterned symbols.

    Defining Packages

    The simplest way to define a package is by evaluating (defpackage package-name), where package-name, which is not evaluated, is either a string, which will be the package's name, or a symbol whose name will be used for the name of the package. We recommend using a keyword symbol, such as (defpackage :test).

    Look at the xemacs buffer in which you've been putting the exercises. Notice "pkg:user" in the mode line.

    Enter (defpackage :test) as the first form in this file, right after your initial comments.

    We want the symbols in this file to be interned into the test package. So, as the Lisp reader is reading this file, we want to change the package to the test package right after that package has been defined. We'll do this by calling the in-package macro, which is what the Listener command :pa calls. The in-package macro takes a string or symbol as its argument, but doesn't evaluate it. We recommend using a symbol of the keyword package, so put (in-package :test) right after the defpackage form, and save the file.

    Notice the package in the mode line hasn't changed (yet). Kill the buffer, and reload the file into xemacs. Notice that the package in the mode line is correct. This is important, because that is the package xemacs will use when you do C-c C-b or C-u C-c C-b.

    Edit your file by changing ":test" to ":exercises" in both places. Save the file. While your cursor is still in the buffer containing the file, enter M-x fi:parse-mode-line-and-package. (Use symbol completion.) Notice that the package in the mode line has now been updated properly.

    Xemacs sets the package of a buffer by finding the first occurrence of a call to in-package in the file. You should not have more than one call to in-package in any file.

    Exercise: Exit from Lisp, and rerun it. Load your file of function definitions. What package is the Listener now in? Test some of your functions.

    When Lisp loads a file, it stores the value of *package*, and restores it after loading the file. That's why you didn't have to call in-package after loading the file to return to the user package.

    Question: Was the Lisp reader "in" the exercises package when it read every form in your file?

    Make the symbols defining the functions in your exercises package external by exporting them:
    Change the form

    (defpackage :exercises)
    to
    (defpackage :exercises
    	    (:export #:average #:discrim #:fact #:quad-roots #:string-1+))
    
    Save this version of the file, exit Lisp, run Lisp again, load your source file, and try using the functions from the user package.

    Using Packages

    One package may use another package. In that case, in the using package every exported symbol of the used package may be referred to without a package qualifier.

    For example, the common-lisp-user package uses the common-lisp package, which is why you can enter common-lisp symbols such as common-lisp:length without typing the package qualifier.
    To see this, evaluate (package-use-list :user).
    The excl package contains many symbols used in the Allegro Common Lisp implementation that are not in the standard common-lisp package, such as excl:exit.

    What packages does the exercises package use?
    Every package that's defined uses the common-lisp package by default. This is important so that pre-defined Lisp functions may be accessed easily.

    In the Lisp Listener, in the user package, evaluate the form (use-package :exercises). Now try using the functions you've defined without the package qualifier.

    You may experiment on your own with defining a package and explicitly listing other packages for it to use. If you explicitly indicate any packages to use, you must explicitly list the common-lisp package also.

    Shadowing Symbols

    Exercise: In your exercises file, define the function last to take a string and return its last character.

    You can't, because last is the name of a function in the common-lisp package, and you're not allowed to redefine these functions. (Note: functions aren't in packages. Reword that statement more carefully.)

    There are a lot of symbols in the common-lisp package. Must you avoid all of them when naming your functions? No!

    Change the package in the Lisp Listener to the exercises package, and shadow cl:last by evaluating (shadow 'last), and then type your definition of last into the Listener. Test it.

    Add your definition of last to your source file, and add the form (:shadow cl:last) to your defpackage form. Also add your last symbol to those being exported.

    Exit Lisp. Rerun it. Load your source file. Test your last function.

    Try to have the user package use the exercises package. There's a conflict. Only one symbol with a given name may be accessible in any package without qualification. You have to choose which one.

  17. Lists and Conses

    Lists are a fundamental data structure in Lisp, from which the language derives its name (LISt Processing).

    A list is an object that holds a sequence of elements, which may be (references to) any Lisp objects. The syntax for lists is (a b c ...). Lists are created by list.

    '()
    '(1 2 3)
    (list 1 2 3)
    

    Notice what Lisp prints for '() -- nil. The symbol nil also represents the empty list, as well as false.

    Exercise: Construct the list containing the two lists (1 2 3) and (4 5 6).

    Accessing elements:

    (first '(1 2 3))
    (second '(1 2 3))
    (third '(1 2 3))
    (nth 5 '(1 2 3 4 5 6 7 8 9 10))
    (rest '(1 2 3))
    (rest (rest '(1 2 3)))
    (nthcdr 0 '(1 2 3 4 5 6 7 8 9 10))
    (nthcdr 5 '(1 2 3 4 5 6 7 8 9 10))
    

    Working with lists:

    (endp '())
    (endp '(1 2 3))
    (endp nil)
    (endp ())
    (listp '())
    (listp '(1 2 3))
    (eql '(1 2 3) '(1 2 3))
    (equal '(1 2 3) '(1 2 3))
    (length '(1 2 3))
    (append '(1 2 3) '(4 5 6))
    (member 3 '(1 2 3 4 5 6))
    (last '(1 2 3 4 5 6))
    (last '(1 2 3 4 5 6) 3)
    (butlast '(1 2 3 4 5 6))
    (butlast '(1 2 3 4 5 6) 3)
    

    Lists are also sequences.

    Exercise: Write a function (reverse l) which returns a list containing the elements of list l in reverse order. (Common Lisp already includes the reverse function, so you must deal with the name conflict again.)

    The basic building block of a list is called a "cons". A cons is an object containing two elements. The elements are called the car and the cdr (for historical reasons). The syntax of a cons is (car . cdr). You can picture a cons such as (1 . 2) as:

    Conses are used to construct (linked) lists in the usual way.

    When we use conses to implement lists, we will often refer to the two elements as the first and the rest, or the head and the tail. A list whose final cdr is not the empty list () is called a "dotted list" (e.g., (1 2 . 3)). A "proper list" has () (i.e., nil) as its final cdr. The function cons constructs cons cells. Since lists are implemented using conses, it follows that cons is also the function to add elements to the front of a list.

    Working with conses:

    (cons 1 2)
    (cons 1 nil)
    '(1 . nil)
    (cons 1 '(2 3))
    (consp '(1 . 2))
    (car '(1 . 2))
    (cdr '(1 . 2))
    (first '(1 . 2))
    (rest '(1 . 2))
    

    Incidentally, conses can also be used to build binary trees.

    Exercise: Construct the binary tree pictured above.

    Exercise: Define a function (flatten2 binary-tree) that returns the list of elements in binary-tree in order.

    Moreover, proper lists can be used to build variable-arity trees -- for example, ((a (b) c) (d ((e)) () f)).

  18. One-Branch Conditionals

    If may be used without an else expression. In this case, the else expression defaults to nil. However, the preferred style in such a case is to use when and unless. In particular, (when test expression...) evaluates the test and, if its result is true, evaluates the expressions in order, using the result of the last expression as the final result. If the test fails, the result is nil. Similarly, (unless test expression...) evaluates its expressions if the result of the test is false.

    Incidentally, many Lisp control structures -- like Lisp programs -- allow a sequence of expressions and take their result from the last expression. These include defun, when, unless, and cond, which we will see next. Control structures that do this are said in the language specification to use an "implicit progn". Perhaps later we'll mention where this terminology originates.

    One-branch conditionals are particularly useful when the default value of a computation is nil. For example:

    (defun member (x list)
      "Returns true if x is a member of list."
      (when list
        (or (eql x (first list)) (member x (rest list)))))
    

    Exercise: Write a function (get-property x list) which returns the element of list immediately following x, or nil if x does not appear in list. For example, (get-property 'name '(name david office 125)) => david. (You might take advantage of the fact that Lisp's builtin member function does not just return t when x is in the list. You can write this without using when, but just for fun, use it anyway.) A list of the form used by this function is traditionally called a property list. Similar builtin functions are getf and get-properties, although they take slightly different arrangements of arguments.

  19. Multi-Branch Conditionals

    The form of the multi-branch conditional is:

    (cond
     (expression11 expression12 ...)
     (expression21 expression22 ...)
     ...
     (expressionn1 expressionn2 ...))
    

    The expressioni1 are evaluated starting at i = 1 until one of them evaluates to any non-null value. If so, the rest of the expressions in that group (if any) are evaluated, and the value of the last one evaluated becomes the value of the cond. If all of the expressioni1 evaluate to nil, then the value of the cond is nil. As is often the case, the value of a Lisp expression is the value of the last subexpression evaluated under its control.

    Most frequently, cond is thought of as:

    (cond
     (test1 expression1 ...)
     (test2 expression2 ...)
     ...
     (testn expressionn ...))
    

    The last test may be t, when it is to be considered the default clause.

    (defun elt (list index)
      "Returns the index'th element of list, or nil if there isn't one."
      (cond
       ((endp list)
        nil)
       ((zerop index)
        (first list))
       (t
        (elt (rest list) (1- index)))))
    

    Exercise: Define (flatten tree) to take an argument list representing a variable-arity tree, and return a list of all the atoms which appear in it. For example (flatten '((a (b) c) () (((d e))))) => (a b c d e).

    Another convenient use of the multi-branch conditional is encapsulated in the case form. Case implements branching on the value of an expression indexed by literal constants (similar to "switch" in other languages). For example, suppose we ask someone to guess a number:

    (case (read)
      (2 "sorry, too low")
      (3 "right on!")
      (4 "sorry, too high")
      (t "way off?!"))
    

    A case form is more or less equivalent to a cond form as follows:

    (case expression
      (literal1 result1)
      (literal2 result2)
      ...
      (literaln resultn))
    

    (cond
      ((eql 'literal1 expression) result1)
      ((eql 'literal2 expression) result2)
      ...
      ((eql 'literaln expression) resultn))
    

    except that expression is only evaluated once. As with cond, the last clause may be labeled with t to indicate that it is the default clause. Also notice the quote in the cond, as the literals are not evaluated in a case form.

    Unlike, C's switch statement, Lisp's case can handle multiple cases giving the same answer without the need for a break. For example,

    (case (read)
      ((#\a #\e #\i #\o #\u) 'vowel)
      (#\y 'sometimes\ vowel)
      (t 'consonent))
    
  20. Local Variables

    Remember the quad-roots function from a few weeks ago?

    (defun quad-roots (a b c)
      "Returns the two roots of the equation ax^2 + bx + c."
      (values (/ (+ (- b) (discrim a b c)) (* 2 a))
    	  (/ (- (- b) (discrim a b c)) (* 2 a))))
    

    It would be preferable to avoid recalculating all the subexpressions of this formula for the two returned values. Local variables are introduced by let.

    (defun quad-roots (a b c)
      "Returns the two roots of the equation ax^2 + bx + c."
      (let ((-b (- b))
            (d  (discrim a b c))
            (2a (* 2 a)))
        (values (/ (+ -b d) 2a) (/ (- -b d) 2a))))
    

    The general form of a let expression is:

    (let ((v1 e1)
          (v2 e2)
          ...
          (vn en))
      expression
      ...)
    

    The variables v1 through vn are bound to the results of the expressions e1 through en. These bindings have effect throughout the body expressions. As usual, the result of the let expression is the result of the last body expression.

    Let bindings are lexically scoped:

    (let ((x 1))
      (list
        (let ((x 2))
          x)
        (let ((x 3))
          x)))
    

    Let bindings are performed in parallel:

    (let ((x 3))
      (let ((x (1+ x))
            (y (1+ x)))
        (list x y)))
    

    A variant of let called let* performs the bindings sequentially.

    (let ((x 3))
      (let* ((x (1+ x))
             (y (1+ x)))
        (list x y)))
    
    
    	
  21. Lambda Lists The list of formal parameters following the function name in a defun form is called a lambda list. The lambda lists we have seen so far contain only required parameters, but there are actually a total of five kinds of parameters that might be included, in the order listed below.
    Required Parameters
    Required parameters are the normal formal parameters you are used to. There must be one actual argument for each required parameter, and the required parameters are bound to the values of the actual arguments in left-to-right order.

    Optional Parameters
    Optional parameters follow the lambda list keyword &optional. Each optional parameter may be
    var
    (var default-value)
    or (var default-value supplied-p)
    If there are more actual arguments than required parameters, the extras are bound to the optional parameters in left-to-right order. If there are extra optional parameters, they are bound to the value of default-value, if it is present, else to nil. If supplied-p is present and there is an actual argument for the optional parameters, it is bound to t, otherwise it is bound to nil.
    Examples:
    1. Notice that the function last takes an optional argument.

    2. Try:
      (defun testOpt (a b &optional c (d 99 dSuppliedp))
        (list a b c d
              (if dSuppliedp '(supplied) '(default))))
      (testOpt 2 3)
      (testOpt 2 3 4 5)
      
    Exercise: Redefine your reverse/reverse1 pair as the single function reverse, that takes one required argument and one optional argument.

    Rest Parameters
    Using required and optional parameters, a Lisp function is restricted to a maximum number of actual arguments. If the lambda list contains the keyword &rest, it must be followed by a simple parameter symbol, and that symbol is bound to a list of the values of all the actual arguments that follow the required arguments.
    Examples:
    1. Notice that the function - has one required parameter and a rest parameter, so it takes one or more arguments.

    2. Notice that and has only a rest parameter, so it takes zero or more arguments.

    3. Try
      (defun testRest (a b &rest c)
        (list a b c))
      (testRest 1 2)
      (testRest 1 2 3 4 5 6)
      

    Exercise: The function union takes two lists and returns a list of all the elements that are in either argument list. Try it. Define your own union function, in your package, that takes zero or more argument lists, and, using cl:union returns the union of all its argument lists.

    Bonus fact: The Lisp function apply takes two arguments: a function, and a list of arguments for the function. apply returns the value of applying the function to those arguments.
    Try:

    (apply #'cons '(a b))
    (apply #'+ '(1 2 3 4))
    

    Keyword Parameters
    A problem with optional parameters would occur if you defined a function with several optional parameters, and the user wanted to call the function with an optional argument other than the first. The first actual argument after the required arguments would be always be assigned to the first optional parameter.

    Keyword parameters are optional, but their arguments may appear in any order, and any one may be supplied or omitted independently of the others.

    Keyword parameters follow the lambda list keyword &key. Each keyword parameter may be

    var
    (var default-value)
    or (var default-value supplied-p)
    A keyword parameter var is used in the body of the function as you would expect, but in the function call, a keyword argument is specified by preceding it with a symbol in the keyword package whose name is the same as the name of the var, that is, :var.

    Exercise:

    1. Try
      (defun testKey (a &key oneKey (twoKey 99 2Suppliedp))
        (list a oneKey twoKey
      	(if 2Suppliedp '(supplied) '(default))))
      (testKey 2)
      (testKey 2 :oneKey 5)
      (testKey 2 :twoKey 5)
      (testKey 2 :twoKey 10 :oneKey 5)
      

    2. Notice that member has two required parameters and three keyword parameters.
      Try
      (member '(a b) '((a c) (a b) (c a)))
      (member '(a b) '((a c) (a b) (c a)) :test #'equal)
      (member 'a '((a c) (a b) (c a)))
      (member 'a '((a c) (a b) (c a)) :key #'second)
      (member 'a '((a c) (a b) (c a)) :key #'second :test-not #'eql)
      

    3. Notice that cl:union also takes three keyword parameters. Modify your arbitrary-argument union to accept the same three keyword parameters, and pass on to cl:union those, but only those, that are passed to your function.

      Bonus fact: The function identity returns the value of its argument.

    Aux Parameters
    Aux parameters follow the lambda list keyword &aux, and are a list of local variables with initializations. The definition
    (defun (var1 ... varn &aux avar1 ... avarm)
      body)
    
    is exactly equivalent to
    (defun (var1 ... varn)
      (let* (avar1 ... avarm)
        body))
    

    Exercise:

    1. Try
      (defun test (x &aux (x (1+ x)) (y (1+ x)))
        (list x y))
      (test 3)
      

    2. Rewrite your quad-roots function to use aux parameters instead of let variables.
  22. Iteration

    There are a number of iteration constructs in Lisp, but only one that you need to know -- the loop facility. Loop subsumes all the other iteration constructs; and it is easier to read and more flexible.

    The simplest kind of loop takes the form: (loop expression...). This repeatedly executes the expressions. The loop is essentially infinite, but you can use return to end it.

    An "extended loop" consists of a sequence of loop clauses. Here is a simple example

    (loop for i from 1 to 10
      do (print (* i i)))
    

    which comprises two clauses: (1) for i from 1 to 10; and (2) do (print (* i i)).

    As you can see, loops don't look like normal Lisp code. Normal Lisp code uses list forms to indicate the structure of the program. In constrast, since loops are more syntactically complex, a number of "loop keywords" are employed to indicate their structure. (Loop "keywords" are not really keyword symbols, but rather syntactic keywords like the keywords of Java, or the keywords of lambda lists -- &rest, etc.) Each kind of loop clause is introduced by a different keyword. Other keywords are used to indicate the internal structure of a compound clause.

    There are seven kinds of loop clauses -- iteration control clauses, termination tests, value accumulation clauses, unconditional execution clauses, conditional execution clauses, initial-final clauses, local variable clauses.

    1. Iteration control clauses

      Iteration control clauses are introduced by the keyword for, determine how a loop variable is "stepped" between each iteration, and arrange for the loop to terminate when finished stepping. There are seven kinds of iteration control clauses. Some of these clauses iterate over common data structures, one iterates numerically, and one is a general-purpose clause.

      1. Numeric ranges: for var from start {to | upto | below | downto | above} end [by incr]

        (loop for i from 99 downto 66 by 3
          do (print i))
        
      2. List elements: for var in list [by step-fun]

        (loop for x in '(a b c d e)
          do (print x))
        
        (loop for x in '(a b c d e) by #'cddr
          do (print x))
        

        An interesting aspect of the loop facility is that it allows destructuring bindings.

        ;; Ignore the format magic used in the following examples for now.
        ;; We'll talk about format later.
        
        (loop for (l n) in '((a 1) (b 2) (c 3) (d 4) (e 5))
          do (format t "~a is the ~:r letter~%" l n))
        
        (loop for (first . rest) in '((42) (a b) (1 2 3) (fee fie foe fum))
          do (format t "~3a has ~d friend~:*~p~%" first (length rest)))
        
      3. List tails: for var on list [by step-fun]

        (loop for x on '(a b c d e)
          do (print x))
        
        (loop for x on '(a b c d e) by #'cddr
          do (print x))
        

        And with destructuring again:

        (loop for (x y) on '(a b c d e f) by #'cddr
          do (print (list x y)))
        
      4. Vector elements: for var across vector

        (loop for c across "david"
          do (print (char-upcase c)))
        
      5. Hash table elements: for var being each {hash-key | hash-value} of hash-table

      6. Package symbols: for var being each {present-symbol | symbol | external-symbol} [of package]

        (loop for x being each present-symbol of *package*
          do (print x))
        
      7. Anything: for var = expression [then expression]

        (loop
          for x from 0 below 10
          for y = (+ (* 3 x x) (* 2 x) 1)
          do (print (list x y)))
        
        (loop
          for l in '(a b c d e)
          for m = 1 then (* 2 m)
          do (format t "the bit mask for ~a is ~d~%" l m))
        
        (loop
          for prev = #\d then next
          for next across "avid"
          do (format t "~a came before ~a~%" prev next))
        

      Iteration control clauses are normally performed sequentially. Stepping can be performed in parallel by connecting the clauses with and rather than for.

    2. Value accumulation clauses

      Normally, loop returns nil. However, value accumulation clauses can determine other values to return.

      List accumulation clauses construct a result list: {collect | append} expression [into var].

      (defun explode (string)
        (loop for c across string collect c))
      
      (defun flatten (tree)
        (if (listp tree)
          (loop for child in tree append (flatten child))
          (list tree)))
      
      (loop for r on '(a b c d e)
        collect (length r)
        append r)
      

      Numeric accumulation clauses compute a result value: {count | sum | minimize | maximize} expression [into var].

      (loop for l in '((1 2 3) () (fee fie foe fum) () (a b c d e))
        for n = (length l)
        count l into count
        sum n into sum
        minimize n into min
        maximize n into max
        do (print (list count sum min max)))
      
      (loop for l in '((1 2 3) () (fee fie foe fum) () (a b c d e))
        for n = (length l)
        maximize n into max
        sum max)
      
      (loop for l in '((1 2 3) () (fee fie foe fum) () (a b c d e))
        count l
        count l
        sum (length l))
      
    3. Initial-final clauses

      (loop
        initially (format t "testing")
        repeat 10 do
        (sleep 0.5)
        (format t ".")
        finally (format t "done~%"))
      

      The finally clause is especially useful for returning a value computed by the loop.

      (loop for l in '((1 2 3) () (fee fie foe fum) () (a b c d e))
        for n = (length l)
        count l into count
        sum n into sum
        minimize n into min
        maximize n into max
        finally (return (values count sum min max)))
      
      ;; just to mess with you
      (loop repeat 5 collect (copy-list foo) into foo finally (return foo))
      

      Exercise: Rewrite the fact function using loop. Rewrite the fibonacci function.

    4. Unconditional execution clauses

      You've already seen one of the two unconditional execution clauses.

      • do expression ...
      • return expression

      The do, initially, and finally clauses are the only places in loop where a sequence of expressions is allowed after a loop keyword. As usual, these are executed sequentially.

    5. Conditional execution clauses

      The form of a conditional execution clause is

      {if | when | unless} test
        selectable-clause {and selectable-clause}*  
      [else
        selectable-clause {and selectable-clause}*]
      [end]
      

      where a selectable-clause is a value accumulation clause or unconditional or conditional execution clause.

      (loop for x in '((1 2 3) 4 (5 6) 7 8)
        if (listp x)
          sum (apply #'* x)
        else
          sum x)
      

      Exercise: Rewrite the get-property function using loop. Explain how your new implementation improves over the old one, assuming that even elements of a property list are meant as keys and odd elements as values.

    6. Termination tests

      • repeat number
      • while test
      • until test
      • always expression
      • never expression
      • thereis expression
      (defun power (x n)
        (loop repeat n
          for y = x then (* y x)
          finally (return y)))
      
      (defun user-likes-lisp-p ()
        (loop initially (format t "Do you like Lisp? ")
          for x = (read)
          until (member x '(y n))
          do (format t "Please type either `y' or `n'. ")
          finally (return (eql x 'y))))
      
      (defun composite-p (n)
        (loop for k from 2 below (sqrt (1+ n))
          thereis (when (zerop (nth-value 1 (floor n k))) k)))
      
      ;; just for fun
      (defun prime-factorization (n)
        (let ((k (composite-p n)))
          (if k
            (append (prime-factorization k) (prime-factorization (floor n k)))
            (list n))))
      

      Exercise: Define a function (split list splitters) that returns a list of chunks of the list that occur between elements of splitters. For example, (split '(1 2 3 4 5 6 7 8 9) '(3 6)) => '((1 2) (4 5) (7 8 9)). (Hint: Use a nested loop.)

      Two other ways of terminating a loop should be mentioned. The form (return [value]) immediately terminates the loop and returns the value. The form (loop-finish) terminates the loop, executes the finally clauses, and returns any accumulated value.

      A loop can be named -- (loop named name clauses...); and such a loop can be terminated using (return-from name [value]) similarly to return. (More precisely, a loop establishes an implicit block of the same name, or nil.)

    7. Local variable clauses

      (loop with s = "david pierce"
        for prev = (char s 0) then next
        for next across (subseq s 1)
        do (format t "~a came before ~a~%" prev next))
      

      With clauses are normally initialized sequentially. Variables can be initialized in parallel by connecting the clauses with and rather than with.

    I'll conclude this lesson with a few additional points about loops.

    • As we have seen, termination conditions can arise in several places -- iteration control clauses, termination test clauses, and even uses of return and loop-finish. Just to clarify, a loop terminates when its first termination condition is satisfied. Depending on the type of termination, the loop may or may not return a value, and may or may not execute the finally clauses.

    • Although loop is quite flexible about the order of clauses, the order is not completely free. The general rule is the "variable" clauses come before "action" clauses. The "variable" clauses are the interation control and with clauses. The "action" clauses are the execution, value accumulation, and termination test clauses. Initial-final clauses may occur anywhere.

  23. Assignment
    Globals
    (defconstant name initial-value [documentation])
    Illegal to change value.

    (defparameter name initial-value [documentation])

    (defvar name [initial-value [documentation]])
    Won't reinitialize the variable.

    Usual style is to name globals *var*

    Try:

    (defconstant *Lab* 'Baldy\ 19
      "Where we meet.")
    *Lab*
    (defconstant *Lab* 'Baldy\ 21
      "Where we meet.")
    *Lab*
    (defparameter *Time* "TTh 1:30-2:30"
      "Time of our meeting")
    *Time*
    (defparameter *Time* "MTh 10:30-1:30"
      "Time of our meeting")
    *Time*
    (defvar *Attendance* 20
      "The number of students attending")
    *Attendance*
    (defvar *Attendance* 6
      "The number of students attending")
    *Attendance*
    

    Assignment
    (set symbol value)
    Evaluates both arguments.

    (setq {symbol value}*)
    Doesn't evaluate symbol. Old fashioned.

    (setf {place value}*)
    Uses l-value of place. Sequential.

    (psetf {place value}*)
    Uses l-value of place. Parallel.

    Try:
    (setf *Lab* 'Baldy\ 19)
    (setf *Time* "TTh 10:30-1:30"
          *Attendance* 10)
    *Time*
    *Attendance*
    
    (setf x 3 y 5) ; Don't assign to new global variables in a function body
    x
    y
    (psetf x y y x)
    x
    y
    

    Places

    May be a symbol, or a wide variety of specifiers of generalized references.
    For example:

    (setf x '(a b c d e))
    (setf (second x) 2)
    x
    
    (setf addresses (make-hash-table))
    (setf (gethash 'Stu addresses) 'shapiro@cse.buffalo.edu)
    (setf (gethash 'David addresses) 'drpierce@cse.buffalo.edu)
    (setf (gethash 'Luddite addresses) nil)
    (gethash 'David addresses)
    (gethash 'Stu addresses)
    (gethash 'Luddite addresses)
    (gethash 'Bill addresses)
    

    But be careful:

    (defun goodTimers (folks)
       (append folks '(had a good time)))
    (setf list1 (goodTimers '(Trupti Mike and Fran)))
    (setf (seventh list1) 'bad)
    list1
    (goodTimers '(Jon Josephine and Orkan))
    

    And don't get carried away. Experienced Lispers use setf very infrequently.

    Some useful globals
    *The last object returned by the Lisp listener.
    **The second-last object returned by the Lisp listener.
    ***The third-last object returned by the Lisp listener.
    *package*The current package.
    *print-base*Numeric base to be used when printing numbers.
    *read-base*Numeric base to be used when reading numbers.
    tpl:*print-length*The maximum length of lists printed at the top level; if nil, no limit.
    tpl:*print-level*The maximum depth of lists printed at the top level; if nil, no limit.

    Exercise: Turn the Lisp listener into a converter from hexidecimal notation to binary notation. Then put it back.

  24. Sequential Execution

    Now that we've looked at assignment, we might as well see the other essential imperative construct -- sequencing. Actually there's nothing new here, because many Lisp forms allow a sequence of expressions in the "body" of the form. This is true of defun, cond, and let, for example.

    Remember also that we told you in passing that the sequence of expressions forming the body of such a construct was called an "implicit progn". That's because progn is the Lisp expression for enclosing an explicit sequence of expressions. The result of a progn is the value of the last expression; it discards the results of the others.

    You won't need to use progn very much, since most of the usual hangouts for sequences of expressions are already implicit progns. However, there are a couple of interesting variations of progn that are occasionally handy, namely prog1 and prog2.

    (prog1 1 2 3)
    (prog2 1 2 3)
    (progn 1 2 3)
    
  25. Functions

    We already know quite a bit about functions -- at least, named functions.

    • Named functions are defined by defun forms.
    • Functions are called by evaluating a list form whose first element is the function name -- (function-name argument ...).
    • The form (function function-name) can be used to obtain a function object for a name. #'function-name is an abbreviation for (function function-name).

    What can we do with function objects in Lisp?

    • Functions can be bound to variables, passed as arguments, and stored in data structures, just like other Lisp objects. Functions with these abilities are often referred to as "first class functions".
    • Functions can be called on arguments argument1 ... argumentn using the form (funcall function argument1 ... argumentn).
    • Functions can be called on arguments argument1 ... argumentn using the form (apply function argument1 ... argumentm-1 argumentsm...n), where argumentsm...n is a list of arguments m through n.

    Some examples we've already seen:

    (member '(a c) '((a b) (a c) (b c)) :test #'equal)
    
    (loop for x in '(a b c d e) by #'cddr do (print x))
    

    Some new examples:

    (funcall #'cons nil nil)
    
    (setf some-functions (list #'third #'first #'second))
    
    (funcall (first some-functions) '(a b c))
    
    (defun multicall (list-of-functions &rest arguments)
      "Returns a list of results obtained by calling each function
    in LIST-OF-FUNCTIONS on the ARGUMENTS."
      (loop for f in list-of-functions
        collect (apply f arguments)))
    
    (multicall (list #'third #'second #'first) '(a b c))
    

    Exercise: Define a function (tree-member item tree &key (key #'identity) (test #'eql)) that returns the subtree of a labeled tree rooted at item, like member does for lists. A labeled tree node is represented as (label . children) where children is a list of nodes. Leaf nodes have empty child lists. An item is regarded as equal to a label of tree when (test item (key label)) is true. For example:

    (tree-member "feline"
      '("animal"
        ("mammal"
         ("feline" ("lion") ("tiger") ("kitty"))
         ("rodent" ("squirrel") ("bunny") ("beaver")))
        ("bird" ("canary") ("pigeon"))
        ("reptile" ("turtle") ("snake")))
      :test #'string=)
    ==> ("feline" ("lion") ("tiger") ("kitty"))
    

    Since function objects can be used so flexibly in Lisp, it makes sense that we should be able to create a function without having to define a named function; that is, use a throwaway function instead of a permanent defun. That's what lambda is for. A "lambda expression" can be used in place of a function name to obtain a function.

    #'(lambda (x) (+ x 1))
    
    ((lambda (x) (+ x 1)) 42)
    
    (funcall #'(lambda (x) (+ x 1)) 42)
    

    Note that

    ((lambda lambda-list . body) . arguments) == (funcall #'(lambda lambda-list . body) . arguments).

    In fact, the function form isn't really necessary, because lambda is set up so that

    (lambda lambda-list . body) == #'(lambda lambda-list . body).

    Lambda functions are actually closures, which means that they comprise not only their code, but also their lexical environment. So they "remember" variable bindings established at the time of their creation.

    (defun make-adder (delta)
      (lambda (x) (+ x delta)))
    
    (setf f (make-adder 13))
    (funcall f 42)
    
    (funcall (make-adder 11) (funcall (make-adder 22) 33))
    

    Exercise: Define a function (compose f g) that composes functions f and g. Assume that f composed with g is defined as (f • g)(x) = f(g(x)). Try (funcall (compose #'char-upcase #'code-char) 100).

  26. Mapping

    It is common to want to apply a function to every element of a list and obtain the results of each of the calls. This operation is called mapping. Lambda expressions tend to be quite useful in mapping.

    (mapcar #'(lambda (s) (string-capitalize (string s))) '(fee fie foe fum))
    
    (maplist #'reverse '(a b c d e))
    
    (mapcar #'(lambda (s n) (make-list n :initial-element s))
    	'(a b c d e) '(5 2 3 7 11))
    
    (mapcan #'(lambda (s n) (make-list n :initial-element s))
    	'(a b c d e) '(5 2 3 7 11))
    
    (mapcon #'reverse '(a b c d e))
    
  27. Sequences

    Sequence is the common superclass of lists and vectors (i.e., one-dimensional arrays) -- one-dimensional ordered collections of objects. Sequences also support mapping.

    (map 'list #'(lambda (c) (position c "0123456789ABCDEF")) "2BAD4BEEF")
    
    (map 'string #'(lambda (a b) (if (char< a b) a b))
         "David Pierce" "Stu Shapiro")
    

    Here are examples of other useful functions on sequences. Many of these take functions as arguments.

    (count-if #'oddp '(2 11 10 13 4 11 14 14 15) :end 5)
    
    (setf x "David Pierce")
    (sort x #'(lambda (c d)
    	    (let ((m (char-code c)) (n (char-code d)))
    	      (if (oddp m)
                    (if (oddp n) (< m n) t)
    	        (if (oddp n) nil (< m n))))))
    ;; note that SORT does destructive modification
    x
    
    (find-if
     #'(lambda (c) (= (* (first c) (first c)) (second c)))
     '((1 3) (3 5) (5 7) (7 9) (2 4) (4 6) (6 8)))
    
    (position-if
     #'(lambda (c) (= (* (first c) (first c)) (second c)))
     '((1 3) (3 5) (5 7) (7 9) (2 4) (4 6) (6 8)))
    
    (reduce #'+ '(1 2 3 4))
    (reduce #'list '(a b c d e))
    (reduce #'list '(a b c d e) :initial-value 'z)
    (reduce #'list '(a b c d e) :from-end t)
    (reduce #'append '((a b) (c d) (e f g) (h) (i j k)))
    

    Exercise: Suppose you are given a list of headings for the columns of a table -- for example, ("Function " "Arguments " "Return values " "Author " "Version "). The size of the columns are determined by the length of these headings. Write an expression to compute the number of spaces to insert to place text into the nth column of the table.

  28. Input/Output

    I/O in Lisp is based on streams. A stream is a source or destination for characters or bytes. For example, streams can be directed to or from files, strings, or the terminal. Output functions (e.g., format and print) and input functions (e.g., read) normally take stream arguments; although frequently the stream argument is optional. Several streams are available when Lisp starts up, including *standard-input* and *standard-output*. If the session is interactive, both of these are the same as *terminal-io*.

    The basic output functions for streams are write-char and write-line. The basic input functions are read-char and read-line.

    File streams are created by the open function. However, it is more convenient to use the with-open-file form, which ensures that the file is closed regardless of whether control leaves normally or abnormally.

    (with-open-file (output-stream "/tmp/drpierce.txt" ; put your name here
                     :direction :output)
      (write-line "I like Lisp" output-stream))
    
    (with-open-file (input-stream "/tmp/drpierce.txt" :direction :input)
      (read-line input-stream))
    
    (with-open-file (output-stream "/tmp/drpierce.txt" 
                     :direction :output
                     :if-exists :supersede)
      (write-line "1. Lisp" output-stream))
    
    (with-open-file (output-stream "/tmp/drpierce.txt" 
                     :direction :output
                     :if-exists :append)
      (write-line "2. Prolog" output-stream)
      (write-line "3. Java" output-stream)
      (write-line "4. C" output-stream))
    
    ;; read lines until eof
    (with-open-file (input-stream "/tmp/drpierce.txt" :direction :input)
      (loop for line = (read-line input-stream nil nil)
        while line
        collect line))
    

    Similarly, a string stream is usually manipulated using with-output-to-string and with-input-from-string.

    (with-output-to-string (output-stream)
      (loop for c in '(#\L #\i #\s #\p)
        do (write-char c output-stream)))
    
    (with-input-from-string (input-stream "1 2 3 4 5 6 7 8 9")
      (loop repeat 10 collect (read-char input-stream)))
    

    Although the basic I/O functions are available, you will normally invoke the higher-level capabilities of the Lisp printer and the Lisp reader. We discuss the printer and reader in the following sections.

    Streams are closed using close. Other stream functions include streamp, open-stream-p, listen, peek-char, clear-input, finish-output.

  29. The Printer

    The standard entry point into the printer is the function write; and prin1, princ, print, and pprint are wrappers for certains settings of write. The optional output stream argument of each of these functions defaults to standard output. Another useful set of print functions is write-to-string, prin1-to-string, and princ-to-string.

    (setf z
      '("animal"
        ("mammal"
         ("feline" ("lion") ("tiger") ("kitty"))
         ("ursine" ("polar bear") ("teddy bear"))
         ("rodent" ("squirrel") ("bunny") ("beaver")))
        ("bird" ("canary") ("pigeon"))
        ("reptile" ("turtle") ("snake"))))
    (prin1 z) ;; same as (write z :escape t)
    (princ z) ;; same as (write z :escape nil :readably nil)
    (write z :escape nil :pretty t :right-margin 40)
    (write-to-string z :escape nil :pretty nil)
    

    A more sophisticated and flexible aspect of the printer is the format function -- (format destination control-string argument...). This function consults the control-string to determine how to format the remaining arguments (if any) and transfers the output to destination.

    If destination is: then the output:
    t appears on the standard output
    a stream appears on that stream
    nil is returned as a string

    The control string consists of simple text, with embedded format control directives. Some of the simpler, more commonly used directives are summarized below.

    ~W format as if by write; any kind of object; obey every printer control variable
    ~S format as if by prin1; any kind of object; "standard" format
    ~A format as if by princ; any kind of object; human readable ("asthetic") format
    ~D (or B, O, X) decimal (or binary, octal, hex) integer format
    ~F (or E, G, $) fixed-format (or exponential, general, monetary) floating-point format
    ~{control-string~} format a list; repeatedly uses control-string to format elements of the list until the list is exhausted
    ~% print a newline
    ~& print a newline unless already at the beginning of the line
    ~~ print a (single) tilde
    ~* ignore the corresponding argument
    ~newline ignore the newline and any following whitespace (allows long format control strings to be split across multiple lines)

    Many format control directives accept "arguments" -- additional numbers or special characters between the ~ and the directive character. For example, a common argument allowed by many directives is a column width. See the documentation for individual directives for details about their arguments. In place of an "argument" in the control string, the character v indicates the next format argument; while the character # denotes the number of remaining format arguments.

    ;; format an invoice
    (loop for (code desc quant price) in
      '((42 "House" 1 110e3) (333 "Car" 2 15000.99) (7 "Candy bar" 12 1/4))
      do (format t "~3,'0D ~10A ~3D @ $~10,2,,,'*F~%" code desc quant price))
    
    (defun char-* (character number)
      "Returns a string of length NUMBER filled with CHARACTER."
      (format nil "~v,,,vA" number character ""))
    ;; but (make-string number :initial-element character) is better
    
    ;; format an invoice again, one-liner
    (format t "~:{~3,'0D ~10A ~3D @ $~10,2,,,'*F~%~}"
     '((42 "House" 1 110e3) (333 "Car" 2 15000.99) (7 "Candy bar" 12 1/4)))
    
    ;; comma-separated list
    (loop for i from 1 to 4 do
      (format t "~{~A~^, ~}~%" (subseq '(1 2 3 4) 0 i)))
    
    ;; comma-separated list again, but cleverer
    ;; (using things I didn't mention above :-)
    (loop for i from 1 to 4 do
      (format t "~{~A~#[~; and ~:;, ~]~}~%" (subseq '(1 2 3 4) 0 i)))
    
    (loop for i from 1 to 4 do
      (format t "~{~A~#[~;~:;,~]~@{~#[~; and ~A~:; ~A,~]~}~}~%"
    	  (subseq '(1 2 3 4) 0 i)))
    
    ;; format an invoice again, but cleverer
    ;; with commas in the prices
    (loop for (code desc quant price) in
      '((42 "House" 1 110e3) (333 "Car" 2 15000.99) (7 "Candy bar" 12 1/4))
      do (format t "~3,'0d ~10a ~3d @ ~{$~7,'*:D~3,2F~}~%"
    	     code desc quant (multiple-value-list (floor price))))
    

    Exercise: Define (print-properties plist &optional stream) to print a propery list to stream as shown below. The stream should default to *standard-output*.

    (print-properties '(course CSE-202 semester "Summer 2004"
    		    room "Baldy 21" days "MR" time (10.30 11.30)))
    -->
    course=CSE-202
    semester="Summer 2004"
    room="Baldy 21"
    days="MR"
    time=(10.3 11.3)
    
  30. The Reader

    The standard entry point into the reader is the function read. The function read-from-string is also often convenient.

    (with-input-from-string (input-stream "(a b c)")
      (read input-stream))
    
    (with-input-from-string (input-stream "5 (a b) 12.3 #\\c \"foo\" t")
      (loop repeat (read input-stream)
        do (describe (read input-stream))))
    

    Below is a reader function for the property list format we used above.

    (defun read-properties (&optional (input-stream *standard-input*))
      "Reads a property list from INPUT-STREAM.
    The input should have one property-value pair on each line,
    in the form PROPERTY-NAME=VALUE.  The PROPERTY-NAME should
    be a Lisp symbol.  The VALUE may be any readable Lisp value."
      (loop for line = (read-line input-stream nil nil)
        while line
        for pos = (position #\= line)
        unless pos do (error "bad property list format ~s" line)
        collect (read-from-string line t nil :end pos)
        collect (read-from-string line t nil :start (1+ pos))))
    
    (setf p1 '(course CSE-202 semester "Summer 2004"
    	   room "Baldy 21" days "MR" time (10.30 11.30)))
    (setf p2 (with-output-to-string (stream)
    	   (print-properties p1 stream)))
    (setf p3 (with-input-from-string (stream p2)
               (read-properties stream)))
    (equal p1 p3)
    

    In practice, we might want more error checking, because read-properties happily accepts input like:

    (with-input-from-string (stream "hello world = 1 2 3")
      (read-properties stream))
    

    However, this entire example is a bit contrived, since if you wanted to store a property list or association list in a file (e.g., as a config file for your application), you would simply write the entire list to the file instead of formatting the data in some unreadable way. Then you could simply use the Lisp reader to reconstruct the entire list.

    We will be able to work more meaningful exercises of printing and reading after we talk about Lisp "objects" -- that is, class instances. Since instances do not have a readable print syntax of their own, a common task is to concoct a readable syntax using readable objects such as lists so that instances can be written out to files and read back in. For now, the following exercise is hopefully a little more meaningful than the property list example.

    Exercise: We decide to use a space-efficient file format for large, sparse arrays. The format is: dimensions default-value index1 value1 index2 value2 .... For example:

    (100 100) 0
    (30 30) 30
    (60 60) 60
    

    Write a function (read-sparse-array &optional input-stream) to read the sparse array format and construct the array.

    Mini-Project: Write an outline formatter. Assume that the input is a sequence of lines; each line begins with n spaces (n ≥ 0) to indicated that it is a heading at outline level n. For example, here is an outline of the lecture notes on I/O:

    Input/output
     Streams
      File streams
      String streams
     Stream input and output functions
     Other stream functions
    The printer
     Print functions
     Format
      Destinations
      Control directives
      Examples
    The reader
    

    Read an outline from an input stream, automatically number and indent it, and print it to an output stream. Below is one possible output format.

       I. Input/output
           A. Streams
               1. File streams
               2. String streams
           B. Stream input and output functions
           C. Other stream functions
      II. The printer
           A. Print functions
           B. Format
               1. Destinations
               2. Control directives
               3. Examples
     III. The reader
    

    Your outline formatter should be guided by an outline format containing a list (F0 F1 ...). Each Fn is a list of the form (width labeler), where width is the width of a label at outline level n and labeler is a function that takes a whole number and returns a label string in the style of level n. For example, the outline above was formatted using an outline format that begins as follows:

    (defparameter *outline-format-1*
        (list
         (list 6 #'(lambda (n) (format nil "~@R." n)))
         ...
    

    Labels of level 0 are 6 characters wide, and the labeler function produces the roman numeral labels shown above. I'll leave it to you to determine what the rest of the entries should be to produce the rest of output shown above.

    First, write a function (read-outline &optional input-stream) that reads the indented outline and constructs a list of the all the lines and their level.

    ((0 "Input/output")
     (1 "Streams")
     (2 "File streams")
     (2 "String streams")
     (1 "Stream input and output functions")
     (1 "Other stream functions")
     (0 "The printer")
     (1 "Print functions")
     (1 "Format")
     (2 "Destinations")
     (2 "Control directives")
     (2 "Examples")
     (0 "The reader"))
    

    Second, write a function (print-outline outline outline-format &optional output-stream) to format this list according to outline-format.

  31. CLOS
    Introduction
    The Common Lisp Object System (see the Common Lisp HyperSpec Chapter on Objects) provides for the definition of classes (with multiple inheritance) and generic (polymorphic) functions.

    We are only going to give a simplified introduction to CLOS. There are many details we will not cover.

    Many (but not all) predefined Common Lisp types are also predefined classes. These are:

    (Find the two classes with multiple parents.)

    For a complete dag of the predefined classes available in ACL, select the menu item

    ACL=>Composer=>Start Composer
    and then select the menu item
    ACL=>Composer=>CLOS=>Show class subclasses
    and enter t in the minibuffer.

    Generic Functions
    A generic function is a collection of methods with the same name, and "congruent" (compatible) lambda lists (approximately: the same number of each kind of parameter; see the Hyperspec Chap. 7.6.4 for all the details), but required parameters may specify the class of their actual arguments.

    Example 1: Let's define a generic function that will cause some Lisp objects to identify their class:

    (defmethod id ((x number))
        "Prints a message identifying numbers."
        "I'm a number.")
    
    (defmethod id ((x sequence))
        "Prints a message identifying sequences."
        "I'm a sequence.")
    
    Test id for several numbers and sequences of different subtypes.
    Test id for several objects that are neither numbers nor sequences.

    As you would expect, the applicable method for the lowest possible class is the one that is used.
    Exercise: add an id method for some subclass of number or sequence, and test that it is used when appropriate.

    When a class has two parent classes, and there is a generic function with a method for each one, which one is used? That is determined by C 's class precedence list. To see the class precedence list of some class, select the menu item

    ACL=>Composer=>CLOS=>Inspect class
    and enter the name of the class in the minibuffer.

    Exercise: Find a class in the above dag that has two parent classes. Define an id method for each parent class. Which method is used for an object of class C ? Inspect class C , and check that its class precedence list is used correctly.

    Example 2: Define a < relation among numbers and symbols, so that lists containing mixed numbers and symbols may be ordered lexicographically. Numbers should be ordered by cl:<, symbols by string<, and any number should be < than any symbol.
    Solution:

    (defpackage :closExercises
      (:shadow cl:<))
    
    (in-package :closExercises)
    
    (defmethod < ((n1 number) (n2 number))
      "Returns t if the number n1 is less than the number n2; else nil."
      (cl:< n1 n2))
    
    (defmethod < ((s1 symbol) (s2 symbol))
      "Returns t if the symbol s1 is less than the symbol s2; else nil."
      (string< s1 s2))
    
    (defmethod < ((n number) (s symbol))
      "Returns t, because numbers are less than symbols."
      t)
    
    (defmethod < ((s symbol) (n number))
      "Returns nil, because symbols are not less than numbers."
      nil)
    
    (defmethod < ((list1 list) (list2 list))
      "Returns t if list1 is less than list2; nil otherwise."
      ;; Lists are ordered lexicographically according to their members.
      (cond
       ((endp list1) list2)
       ((endp list2) nil)
       ((< (first list1) (first list2)) t)
       ((< (first list2) (first list1)) nil)
       (t (< (rest list1) (rest list2)))))
    
    Exercise: Test this.
    A generic function may be used in the same ways a regular function is used. For example, we may define > as follows:
    ;;; First shadow cl:>.
    (shadow 'cl:>)
    
    ;;; Then define >.
    (defun > (x y)
      "Returns t if x is greater than y; nil Otherwise."
      (< y x))
    
    Notice that > automatically handles the same classes as < does.

    Now, let's redo < using defgeneric and adding strings and lists. Strings should sort after symbols; lists should sort after strings. That is, any number should be < any symbol, any symbol should be < any string, any string should be < any list, numbers should be ordered by cl:<, symbols and strings should be ordered by string<, and lists should be ordered as shown above. (Do we really need to write 16 different methods?)
    Solution:

    (defpackage :closExercises
      (:shadow cl:< cl:>))
    
    (in-package :closExercises)
    
    (defgeneric < (obj1 obj2)
      (:documentation "Returns t if obj1 sorts as less than obj2; nil otherwise.")
    
      (:method ((n1 number) (n2 number))
    	   "Returns t if the number n1 is less than the number n2
                          using cl:<; else nil."
    	   (cl:< n1 n2))
    
      (:method ((s1 symbol) (s2 symbol))
    	   "Returns t if the symbol s1 is less than the symbol s2
                          using string<; else nil."
    	   (string< s1 s2))
      
      (:method ((s1 string) (s2 string))
    	   "Returns t if the string s1 is less than the string s2
                          using string<; else nil."
    	   (string< s1 s2))
    
      (:method ((list1 list) (list2 list))
    	   "Returns t if list1 is lexicographically less than list2; nil otherwise."
    	   ;; Lists are ordered lexicographically according to their members.
    	   (cond
    	    ((endp list1) list2)
    	    ((endp list2) nil)
    	    ((< (first list1) (first list2)) t)
    	    ((< (first list2) (first list1)) nil)
    	    (t (< (rest list1) (rest list2)))))
      
      (:method ((obj1 t) (obj2 t))
    	   "Returns t if ojb1 is less than obj2 according to their types."
    	   (check-type obj1 (or number symbol string list))
    	   (check-type obj2 (or number symbol string list))
    	   (member obj2
    		   (member obj1 '(number symbol string list) :test #'typep)
    		   :test #'typep)))
    
    (defun > (x y)
      "Returns t if x is greater than y; nil Otherwise."
      (< y x))
    
    New concept: check-type.

    Exercises:

    1. Test this.

    2. Add characters, to sort between numbers and symbols, and, among themselves using char<.

    Classes
    An object (an instance of a class) is created by (make-instance class ...).

    CLOS classes are defined by use of the macro defclass. (See also the HyperSpec chapter on Defining Classes.)

    A class may have up to three class-options, the only one of which we will use is :documentation.

    A class may also have a set of slots, each of which has a number of attributes specified by slot-options, which are:

    • :documentation A documentation string.

    • :allocation Either :instance, meaning that this is a slot local to each instance, or :class, meaning that this slot is shared among instances.

    • :initarg A symbol used like a keyword in make-instance to provide the value of this slot.

    • :initform A form, evaluated when each instance is created, to give the value for this slot.

    • :reader A symbol which is made the name of a method used to get the value of this slot for each instance.

    • :writer A symbol which is made the name of a method used to set the value of this slot for each instance. If setSlot is the symbol, it is used by evaluating (setSlot value instance)

    • :accessor A symbol which is made the name of a setfable method used to get or set the value of this slot for each instance.

    • :type The permitted type of values of this slot. ACL seems not to enforce this.

    Even if neither a :writer nor an :accessor is provided for a slot, one my set or change the value of a slot by evaluating

    (setf (slot-value object slot-name) value)
    

    One may use

    (defmethod initialize-instance :after ((object class) &rest args)
        ...)
    
    to cause the initialization of some slots of an instance after the slots specified by :initarg and :initform have been set.

    As an example, we will define some classes of weighable solids and a class of scales on which to weigh them. These are defined in the file solids.cl.

    Exercises:

    1. Copy solids.cl into a file and test it.

    2. Use Composer's inspector to inspect the scale and some of the objects on it.

    3. Add a slot to the class of scales that is a list of the objects on the scale. Make sure that it is properly maintained.

    4. Add a method, (removeObject scale object) to remove an object from a scale. All slots should be maintained properly, but removeObject should cause an error if the object to be removed is not on the scale.

    5. Again use Composer's inspector to inspect a scale with some objects on it. Note that left-clicking on an object will cause Composer to bring up an inspector window on that object.

Copyright © 2004 by Stuart C. Shapiro & David R. Pierce. All rights reserved.

Stuart C. Shapiro <shapiro@cse.buffalo.edu>
David R. Pierce <drpierce at cse.buffalo.edu>
Last modified: Tue Jun 4 14:04:31 2013