Common Lisp

Table of Contents

Common Lisp the language: http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/cltl2.html

1 Type

Common Lisp is strong typed, but the type is associated with objects, not variables.

Type predicate

(typep obj type)
(subtypep type1 type2)

1.1 Numbers

Representation:

  • #b010101
  • #xaf08

1.1.1 Predicates

Group 1:

  • numberp
  • integerp
  • rationalp
  • floatp
  • realp
  • complexp

Group 2:

  • zerop
  • plusp
  • minusp
  • oddp
  • evenp

1.1.2 Comparison

These operators are functions, they takes multiple numbers.

  • =
  • /=: all numbers are different
  • <, >, <=, >=: increasing, decreasing, etc
  • max
  • min

1.1.3 Arithmetic

  • +, -, *, /
  • 1+, 1-: (1+ x) same as (+ x 1)
  • incf place
  • decf place
  • conjugate
  • gcd: greatest common divisor
  • lcm: least common multiple

1.1.4 Other

  • exp
  • expt
  • log
  • sqrt
  • isqrt
  • abs
  • phase
  • signum
  • sin, cos, tan
  • cis
  • asin, acos, atan
  • CONSTANT pi
  • sinh, cosh, tanh, asinh, acosh, atanh

1.1.5 Type Conversion

  • float
  • rational
  • rationalize
  • numerator
  • denominator
  • floor: toward negative infinity
  • ceiling: toward positive infinity
  • truncate: toward 0
  • round: to nearest integer
  • mod
  • rem
  • ffloor
  • fceiling
  • ftruncate
  • fround
  • decode-float
  • scale-float
  • float-radix
  • float-sign
  • float-digits
  • float-precision
  • integer-decode-float
  • complex
  • realpart
  • imagpart

1.1.6 Logical Operations

  • logior
  • logxor: exclusive or
  • logand
  • logeqv
  • lognand
  • lognor
  • logandc1
  • logandc2
  • logorc1
  • logorc2
  • boole
  • lognot
  • logtest
  • logbitp
  • ash
  • logcount
  • integer-length

1.1.7 Byte

  • byte
  • byte-size
  • byte-position
  • ldb
  • ldb-test
  • mask-field
  • dpb
  • deposit-field

1.1.8 Random Numbers

  • random
  • *random-state*
  • make-random-state
  • random-state-p

1.2 Character

A character object can be notated by writing #\c where c is any standard character.

numeric analog case-sensitive case-insensitive string case sens string case insens
= char= CHAR-EQUAL string= string-equal
/= char/= CHAR-NOT-EQUAL string/= string-not-equal
< char< CHAR-LESSP string< string-lessp

1.3 Boolean

  • nil is false, everything else is true
  • nil is both an atom and a list. () is exactly the same as nil

1.4 Symbols

In addition to letters and numbers, the following characters can also be considered to be alphabet:

+ - * / @ $ % ^ & _ = < > ~ .

But some conventions apply:

  • +global-constant+
  • *global-variable*

The following characters are also alphabet, not used by common lisp standard, but reserved for some purpose:

? ! [ ] { }

Finally, lisp is case-insensitive. The program will be converted to upper case when stored in computer.

1.4.1 Property list

A symbol has such a list.

  • get
  • remprop
  • symbol-plist
  • getf
  • remf
  • get-properties

1.5 Sequence

Sequence contains both lists and vectors. Here are the common operations that shares between them because they operate on ordered sets of elements.

1.5.1 Predicates

  • consp
  • listp
  • bit-vector-p
  • vectorp
  • simple-vector-p
  • simple-bit-vector-p
  • arrayp

1.5.2 Simple Sequence Functions

  • elt
  • subseq
  • copy-seq
  • length
  • reverse
  • nreverse
  • make-sequence

1.5.3 Mapping

  • concatenate
  • map
  • map-into
  • some
  • every
  • notany
  • notevery
  • reduce

1.5.4 Modifying Seq

  • fill
  • replace
  • remove
  • remove-if
  • remove-if-not
  • delete
  • delete-if
  • delete-if-not
  • remove-duplicates
  • delete-duplicates
  • substitute
  • substitute-if
  • substitute-if-not
  • nsubstitute
  • nsubstitute-if
  • nsubstitute-if-not

1.5.5 Searching

  • find
  • find-if
  • find-if-not
  • position
  • position-if
  • position-if-not
  • count
  • count-if
  • count-if-not
  • mismatch
  • search

1.5.6 Sorting & Merging

  • sort
  • stable-sort
  • merge

1.6 List

1.6.1 cons

  • car
  • cdr
  • caar, …, cdddr: all combinations, up to 4-level deep
  • cons
  • tree-equal

1.6.2 lists

  • endp: predicate to check the end of a list
  • list-length
  • nth
  • first, second, …, tenth
  • rest
  • nthcdr
  • last
  • list
  • list*
  • make-list
  • append
  • copy-list
  • copy-alist
  • copy-tree: copy only copies the outer-most level of a list. Use copy-tree to copy all levels of a list.
  • revappend
  • nconc
  • nreconc
  • push: macro
  • pushnew
  • pop: macro
  • butlast
  • nbutlast
  • ldiff

1.6.3 alteration of list structure

Change the car or cdr of an existing cons. It is destructive.

  • rplaca
  • rplacd

1.6.4 substitution

  • subst
  • subst-if
  • subst-if-not
  • nsubst
  • nsubst-if
  • nsubst-if-not
  • sublis
  • nsublis

1.6.5 Set

  • member
  • member-if
  • member-if-not
  • tailp
  • adjoin
  • union
  • nunion
  • intersection
  • nintersection
  • set-difference
  • nset-difference
  • set-exclusive-or
  • nset-exclusive-or
  • subsetp

1.6.6 Association Lists

  • acons
  • pairlis
  • assoc
  • assoc-if
  • assoc-if-not
  • rassoc
  • rassoc-if
  • rassoc-if-not

1.7 Hash Table

This is a map.

  • make-hash-table
  • hash-table-p
  • gethash
  • remhash: remove hash entry
  • maphash
  • clrhash
  • hash-table-count
  • with-hash-table-iterator
  • hash-table-rehash-size
  • hash-table-rehash-threshold
  • hash-table-size
  • hash-table-test

1.8 Array

Array can be general array, holding arbitrary object types; it can also be a specialized array that hold a given type. One dimentional arrays are called vectors. Vectors holding arbitrary objects are general vectors, while the ones holding type string-char are called strings, holding type bit are called bit-vectors.

1.8.1 Creation

  • make-array
  • array-rank-limit: constant
  • array-dimension-limit: constant
  • array-total-size-limit: constant
  • vector

1.8.2 Access

  • aref
  • svref

1.8.3 Information

  • array-element-type
  • array-rank
  • array-dimension
  • array-dimensions
  • array-total-size
  • array-in-bounds-p
  • array-row-major-index
  • row-major-aref
  • adjustable-array-p

1.8.4 bit-array

  • bit
  • sbit
  • bit-and
  • bit-ior
  • bit-xor
  • bit-eqv
  • bit-nand
  • bit-nor
  • bit-andc1
  • bit-andc2
  • bit-orc1
  • bit-orc2
  • bit-not

1.8.5 Fill

The fill pointer is a non-negative integer no larger than the total number of elements in the vector (array-dimension). It is the number of filled-in elements in the vector.

  • array-has-fill-pointer
  • fill-pointer
  • vector-push
  • vector-push-extend
  • vector-pop

1.8.6 Change dimension

  • adjust-array

1.9 String

A string is a specialized vector (one-dimensional array) whose elements are characters.

Some predicates

  • characterp
  • stringp
  • simple-string-p

1.9.1 Access

  • char
  • schar

1.9.2 Comparision

  • string=
  • string-equal
  • string<
  • string>
  • string<=
  • string>=
  • string/=
  • string-lessp
  • string-greaterp
  • string-not-greaterp
  • string-not-lessp
  • string-not-equal

1.9.3 String Construction and Manipulation

  • make-string
  • string-trim
  • string-left-trim
  • string-right-trim
  • string-upcase
  • string-downcase
  • string-capitalize
  • nstring-upcase
  • nstring-downcase
  • nstring-capitalize
  • string

1.10 Structure

1.10.1 defstruct

  • MACRO defstruct
  • [ ] automatically generated constructor function. defstruct foo will also define make-foo

2 Program Structure

2.1 Form

Forms are the building block of lisp program. There're three kinds of forms:

  • self-evaluating forms: numbers, nil, :key
  • symbols: variables
  • lists
    • special forms
    • macro calls
    • function calls

2.2 Scope and Extent

Although the global variable can be referred at any place, the binding is still quite lexical regarding to the binding form. E.g, the let binding can rebind the global variable, and everything before the return of let form sees this binding. After the return, the binding fall back to the previous binding. This is good because when you want to temporary change the *standard-output* to a file, you don't need to have to remember to change it back.

This also means, assign to global variable only modify the specific binding, while the binding on the stack does not change. Lisp did this by looking up the name of variable: if it is declared by defvar or defparameter, it will creates dynamic binding.

The symbol is a reference of the object. Assigning to the symbol will create another reference to another object. But, if the object is mutable, then assign to the reference will change the object. Function parameters are reference. So if the object is mutable, then assigning to the parameter will change the referenced object.

2.3 Variable

2.3.1 Creation

(defparameter *varname* init-value "Optional document string")
(defvar *varname* optional-init-value "optional document string")
(defconstant +name+ init-value "optional document string")

Global variable can be defined by defvar and defparameter. Naming convention is put * surrounds it. The difference (Prefer defvar):

  • defparameter will always assign the initial value
  • defvar will do so only if the variable is not defined; defvar can also be used without initial value, the variable will be unbound.

defconstant is used to declare constant. Use + surrounds it. It is possible to redefine the constant using defconstant again, but the behavior is undefined. E.g. the code refer to it might need to be reevaluated to see the update. So, do NOT redefine a constant, otherwise it is not a constant, use defparameter instead.

2.3.2 Assignment

Assigning a value to a binding is:

  1. change the binding only, do not change other hidden bindings for this symbol
  2. do not change the value object the binding refers to

The general assignment operator is setf (place value)+. When assigning a binding, it will call setq (but don't call setq directly!), and returns the newly assigned value. In the document, a SEFTable thing is suitable to be a setf place. Always use setf instead of setq. This is more general. This includes variables, array locations, list elements, hash table entries, structure fields, and object slots.

To make the code more concise, some "f-family" are invented.

(incf x)
(setf x (+ x 1))
(decf x)
(incf x 10)

here incf and decf modifies the argument, so they are called modify macros. Other modify macros:

  • push, pop, pushnew
  • rotatef, shiftf
    • (roratef a b) is equal to (let ((tmp a)) (setf a b b tmp) nil)
    • (shiftf a b 10) shifts all the values left, equals to (let ((tmp a)) (setf a b b 10) tmp)

2.3.3 Destructive

There are two types of destructive functions:

  • for-side-effect: typically use setf
  • recycling operation

The recycling operations are typically those with n as prefix. 80 percent of the use cases are PUSH/NREVERSE and SETF/DELETE.

(defun upto (max)
  (let ((result nil))
    (dotimes (i max)
      (push i result))
    (nreverse result)))
(setf foo (delete nil foo))

sort is also destructive, so use it on a copy of the list. Be sure to assign it back to the variable.

(defparameter *list* (list 4 3 2 1))
(sort *list* #'<) ;; (1 2 3 4)
*list* ;; (4)
;; so shoud use:
(setf *list* (sort *list* #'<))

2.4 Equality

  • EQ tests for object identity. Two objects are EQ if they're identical. It CANNOT compare numbers and characters, which gives undefined behavior.
  • EQL is similar to EQ except that it guarantees the same numeric or character value is equal. (eql 1 1) is t.
  • EQ is more efficient than EQL because it does not need to check whether it is numeric or character. But EQL has less trouble to understand .. so use EQL when possible.
  • EQUAL is looser than EQL. It consider strings equivalent if they contain the same characters.
  • EQUALP is even looser. For example, it consider two strings are equal case-insensitively. NEVER use this.

2.5 Function

2.5.1 Defun and Lambda Expression

defun is a macro.

(defun name (a b
             &optional op1 (op2 def-value) (op3 def-value op3-supplied-p)
             &rest rests
             &key k1 (k2 def-value k2-supplied-p) ((:kkkkk3 k3) def-value k3-supplied-p))
  (body-forms))

lambda expression shares the same structures.

(lambda
    (a b &optional op1 &rest rests &key k1)
  (body))
  • Order of consumption: First required arguments are consumed, then the optional arguments, then the rest, finally the keyword arguments.
  • optional arguments: can have default values (which defaults to nil), and a variable to indicate whether it is supplied.
  • rests: a list.
  • keyword arguments: are the same as optional arguments, except it must be supplied by keyword. It can be rebound to a simpler name to be used in the body.
  • Mixture: Never mix (optional, key). You can mix rest and key, but the behavior is, after matching all required and optional, everything are bound to rest. Then appropriate ones are ALSO bound to keyword arguments.

The return value of function is typically the last expression. But you can explicit return from a function by using RETURN-FROM SYMBOL body special form. Symbol is the function name to return, and it is not evaluted. You must provide the function in order to return, which makes it not frequently used.

The function object can be obtained by #'. One can apply the object in two ways:

FUNCALL
the first is the function object, the rests are arguments
APPLY
the first is the function object, then a list of arguments. The list can be looser, e.g. some arguments, as long as the last one is a list.

2.5.2 Multiple Values

If return multiple values, use values instead of a list; if return no values, use (values)

  • values
  • multiple-values-limit
  • values-list
  • multiple-value-list
  • multiple-value-call
  • multiple-value-prog1
  • multiple-value-bind
  • multiple-value-setq
  • nth-value

2.6 Macro

2.6.1 Definition

defmacro name lambda-list [[ {declaration}* | doc-string ]] {form}*

lambda-list is parameter list.

2.6.2 Macro Expansion

  • macroexpand
  • macroexpand-1

2.7 Exception

2.7.1 Error

  • error
  • cerror
  • warn
  • *break-on-warnings*
  • break
  • check-type
  • assert
  • etypecase
  • ctypecase
  • ecase
  • ccase

2.7.2 Condition

2.7.2.1 TODO Concepts
2.7.2.2 Signaling
  • error
  • warn
  • cerror
  • signal
  • *break-on-signals*

Assertions

  • check-type
  • assert

Exhaustive Case Analysis

  • etypecase
  • ctypecase
  • ecase
  • ccase
2.7.2.3 Handling Conditions
  • hanlder-case
  • ignore-errors
  • handler-bind
2.7.2.4 Defining Conditions
  • define-condition
  • make-condition
2.7.2.5 Restart
  • with-simple-restart
  • restart-case
  • restart-bind
  • with-condition-restarts
  • compute-restarts
  • restart-name
  • find-restart
  • invoke-restart
  • invoke-restart-interactively

Restart functions

  • abort
  • continue
  • muffle-warning
  • store-value
  • use-value
2.7.2.6 Debugging
  • break
  • invoke-debugger
2.7.2.7 Condition Types
  • TYPE restart
  • TYPE condition
  • TYPE warning
  • TYPE serious-condition
  • TYPE error
  • TYPE simple-condition
  • TYPE simple-warning
  • TYPE simple-error
    • simple-condition-format-string
    • simple-condition-format-arguments
  • TYPE storage-condition
  • TYPE type-error
    • type-error-datum
    • type-error-expected-type
  • TYPE simple-type-error
  • TYPE program-error
  • TYPE control-error
  • TYPE package-error
    • package-error-package
  • TYPE stream-error
    • stream-error-stream
  • TYPE end-of-file
  • TYPE file-error
    • file-error-pathname
  • TYPE cell-error
    • cell-error-name
  • TYPE unbound-variable
  • TYPE undefined-function
  • TYPE arithmetic-error
    • arithmetic-error-operation
    • arithmetic-error-operands
  • TYPE division-by-zero
  • TYPE floating-point-overflow
  • TYPE floating-point-underflow

2.8 Evaluator

  • eval form: evaluate form in the current dynamic environment and a null lexical environment
  • evalhook
  • applyhook

3 Control Structure

3.1 Sequential

  • progn
  • prog1
  • prog2

3.2 Conditional

(if condition then-form [else-form])
(progn forms*)
(when cond forms*)
(unless cond forms*)
(cond (test-1 form*) (test-2 form*))

cond corresponds to switch statement in C. The test predicates are evaluated one by one until one to t, then evaluate the body form, and return the last. To have a default, put a t as the last condition.

Lisp programmers often use the functions and and or to implement simple conditional evaluation. For example,

;; use
(and x (setf y t))
;; instead of
(when x
  (setf y t))
;; use
(or x (setf y t))
;; instead of
(unless x
  (setf y t))

3.3 Iteration

(dolist (var list-form) body-form*)
(dotimes (var count-form) body-form*)
(do (var-def*) (end-test-form result-form*) statements*)

dotimes from 0 to the value of count-form-1, inclusively In do, the var-def is (var init-form step-form). For example:

(do ((i 0 (1+ i))) ((> i 4)) (print i))

3.3.1 Mapping

  • mapcar
  • maplist
  • mapc
  • mapl
  • mapcan
  • mapcon

3.3.2 Append to a list

Remember that append copies its arguments. Avoid using append inside a loop to add elements to the back of a list. Use the collect clause in loop, or push elements onto a list and then nreverse the list to return the original ordering.

Bad:

(let ((result ()))
  (dolist (x list)
    (setf result (append result (list x))))
  result)

Better:

(let ((result ()))
  (dolist (x list)
    (push x result))
  (nreverse result))

Best:

(loop for x in list collect x)

3.4 Loop Facility

Loop keywords are not true common lisp keywords. They are symbols recognized only by Loop Facility. If you do not use any loop keywords, the loop simply runs forever.

loop is a macro, and expansion produces an implicit block named nil, and it accepts three basic part in its tagbody:

  • loop prologue: execute before iteration begin
  • loop body: execute during each iteration
  • loop epilogue: execute after iteration termination

All variables are initialized in the loop prologue.

3.4.1 Loop Clauses

Inside the loop is the loop clauses.

Variable initialization and stepping

  • for
  • as
  • with
  • repeat

Value accumulation

  • collect
  • append
  • nconc
  • sum
  • count
  • minimize
  • maximize

Termination conditions

  • loop-finish
  • for
  • as
  • repeat
  • while
  • until
  • always
  • never
  • thereis

Unconditional execution

  • do
  • return

Conditional execution

  • if
  • when
  • unless
  • else
  • end

Miscellaneous

  • named
  • initially
  • finally

3.4.2 Loop Syntax

loop ::= (loop [named name] {variables}* {main}*)
variables ::= with | initial-final | for-as | repeat
main ::= unconditional | accumulation | conditional | termination | initial-final
initial-final ::= initially | finally
  • A loop must have at least one clause.
  • loop prologue
    • automatic variable initializations prescribed by variable clauses
    • initially
  • loop epilogue
    • finally
    • implicit return value from accumulation clause or an end-test clause

3.4.3 Iteration Control (for, as, repeat)

for and as are exctly the same.

Multiple these control can be used. They will occur sequentially: they will not nest.

for var
  [{from | downfrom | upfrom} expr1]
  [{to | downto | upto | below | above} expr2]
  [by expr3]
  • from: default to 0 when increment
  • by: the step, must be positive integer, default to 1
  • downfrom, upfrom, downto, upto: control the direction of increment or decrease.
  • below, above: similar to upto, downto, but do not include the target.
for var in expr1 [by step-fun]
  • it is meant to iterate the list. Bound to element in each iteration
  • At the end of each iteration, the step-fun is executed on the list to produce a successor list. default to cdr.
for var on expr1 [by step-fun]
  • same as in-by, but var is bound to the entire list each time
for var = expr1 [then expr2]
  • var is set to expr1 on first iteration
  • var is set to expr2 on second and subsequent iterations. If no expr2, expr1 is still used.
for var across vector
  • bind to each element. The only difference is now using vector instead of a list.
for var being
  {each | the}
  {hash-key | hash-keys | hash-value | hash-values}
  {in | of}
  hash-table
  [using ({hash-value | hash-key} other-var)]
  • it seems that each and the is the same. Just to make it easy to read:
    • use each for hash-key and hash-value
    • use the for hash-keys and hash-values
  • in and of are also the same
  • hash-key and hash-value controls whether to bind key or value to var
  • using will bind the other part, i.e. value if hash-key and key if hash-value, to another variable for access
for var being
  {each | the}
  {symbol | present-symbol | external-symbol | symbols | present-symbols | external-symbols}
  {in | of}
  package

In package.

repeat expr

repeat the body (expr) times.

3.4.4 End Test Control (always, never, thereis, until, while)

always, never, thereis change the return value, so

  • it will skip finally clauses.
  • NEVER use it with collect, etc.

The clauses:

  • while expr
  • until expr: equal to while (not expr)
  • always expr: terminate if expr evaluates to nil. Return nil if so. Otherwise return t.
  • never expr: terminate if expr ever evalutes to non-nil. Return nil if so, otherwise return t
  • thereis expr: Same as never, but it return that expr.
  • loop-finish: terminate iteration and return any accumulated result

3.4.5 Value Accumulation

  • multiple accumulation can be used if they operate the same type, e.g. collect and append operate on list. The result will be combined, i.e. they operate on the same list.
  • If into is not provided, all the operations operate on a default hidden variable.
  • If into is provided, the variable is as-if initialized in with clause.
    • will not have a default value to return
    • the variables are visible in finally clause
  • Only one value can be returned, but you can return multiple objects using values.

Clauses: all of them have xxx expr [into var] format

  • collect expr [into var]
  • collecting expr [into var]: same as collect
  • append
  • appending
  • nconc
  • nconcing
  • count
  • counting
  • sum
  • summing
  • maximize
  • maximizing
  • minimize
  • minimizing

3.4.6 Variable Initialization (with)

with var [= expr] {and var [= expr]}*
  • if no =expr, it is initialized to appropriate default value
  • by default with initialize variable sequentially
  • using loop keyword and can make the initialization in parallel

3.4.7 Conditional Execution (if, when, unless)

They all have the same signature:

if expr clause {and clause}*
  [else clause {and clause}*]
  [end]
  • if and when are exactly the same. unless is equal to if (not expr).
  • in the case of nest, the else is paired with the closest preceding when or if that has no associated else
  • loop keyword it can be used to refer to the value of the test expr. This is a keyword, thus cannot be used as a variable name in loop.
  • end marks the end of the clause. If not specified, the next loop keyword marks the end. This is useful in compound clauses.

3.4.8 Unconditional Execution (do, return)

  • do {expr}*: execute sequentially
  • doing {expr}*
  • return expr: equivalent to do {return expr}

3.4.9 Misc (named, initially, finally)

  • named: name a loop so that we can use return-from
  • initially, finally: expressions to be evaluated before and after loop body. There can be multiple these clauses, all of them will be collected into one place inside progn in the order they present.
  • return, always, never, thereis can bypass finally

3.4.10 Destructure

bind result to a list of variables. This can be used in for and with.

  • If variable list is shorter, the rest values are discarded
  • If value list is shorter, the rest variables initialize to default value

4 System Interface

4.1 Package

This is used to solve name conflict.

  • *package*
  • make-package
  • in-package
  • find-package
  • package-name
  • package-nicknames
  • rename-package
  • package-use-list
  • package-used-by-list
  • package-shadowing-symbols
  • list-all-packages
  • delete-package
  • intern
  • find-symbol
  • unintern
  • export
  • unexport
  • import
  • shadowing-import
  • shadow
  • use-package
  • unuse-package
  • defpackage
  • find-all-symbols
  • do-symbols
  • do-external-symbols
  • do-all-symbols
  • with-package-iterator

4.1.1 Modules

A module is a subsystem. It consists of one or more packages. It may be loaded from one or more files.

  • *modules*
  • provide
  • require

4.2 Stream

Some global variables are used by many functions. Conventionally the suffix -input and -output means the input and output stream respectively, while -io represents streams with bidirectional stream.

  • *standard-input*
  • *standard-output*
  • *error-output*
  • *query-io*
  • *debug-io*
  • *terminal-io*
  • *trace-output*

4.2.1 Create Stream

  • make-synonym-stream
  • make-broadcase-stream
  • make-concatenated-stream
  • make-two-way-stream
  • make-echo-stream
  • make-string-input-stream
  • make-string-output-stream
  • get-output-stream-string
  • with-open-stream
  • with-input-from-string
  • with-output-to-string

4.2.2 Operation

  • streamp
  • open-stream-p
  • input-stream-p
  • output-stream-p
  • stream-element-type
  • close
  • broadcase-stream-streams
  • concatenated-stream-streams
  • echo-stream-input-stream
  • echo-stream-output-stream
  • synonym-stream-symbol
  • two-way-stream-input-stream
  • two-way-stream-output-stream
  • interactive-stream-p
  • stream-external-format

4.3 Input/Output

These input/output operations perform on streams.

4.3.1 Input

Input stream defaults to *standard-input*.

  • read
  • read-preserving-whitespace
  • read-delimited-list
  • read-line
  • read-char
  • unread-char
  • peek-char
  • listen
  • read-char-no-hang
  • clear-input
  • read-from-string
  • parse-integer
  • read-byte

4.3.2 Output

  • write
  • prin1
  • print
  • pprint
  • princ
  • write-to-string
  • prin1-to-string
  • princ-to-string
  • write-char
  • write-string
  • write-line
  • terpri
  • fresh-line
  • finish-ouptut
  • force-output
  • clear-output
  • print-unreadable-object
  • write-byte
4.3.2.1 Format
format destination control-string &rest arguments

Format output the control-string except that a tilde introduces a directive. Most directives use one or more elements of arguments. If no more arguments, signal an error. But it is ok is more arguments are provided and unprocessed.

If the destination is nil, a string is created as the output and get returned. Otherwise format returns nil.

A format directive is determined by one single character. It can take optional prefix. The prefix can be separated using : or @ or both. Parameters are separated by comma, and they can be ommited to take the default value. What kind of parameters are accepted is determined by the directive character.

~[[first-param]{,[second-param]}*]
 [:@]
 <char>

Here are the list of all directive characters

  • A: Ascii
  • S: S-expression
  • D: Decimal
  • B: Binary
  • O: octal
  • X: hexadecimal
  • R: Radix
  • P: Plural
  • C: Character
  • F: fixed format floating point
  • E: Exponential floating point
  • G: general floating point
  • $: dollars floating point
  • %: #\Newline
  • &: refresh line. unless at the beginning of a line,output a line.
  • |: page separator
  • ~: output a tilde
  • <newline>: ignore the newline and any following whitespace
  • T: tabulate
  • *: ignore next argument
  • ?: indirection
  • _: conditional newline
  • W: wite
  • I: indent

There are several more complicated ones not recorded here, I believe I'll not easily use them.

4.3.3 Query

  • y-or-n-p
  • yes-or-no-p

4.4 File System

4.4.1 File Names

There's a type called pathname. It always has 6 components.

  • host
  • device
  • directory
  • name
  • type
  • version

Extended Wildcards

  • wild-pathname-p
  • pathname-match-p
  • translate-pathname

Functions

  • pathname
  • truename
  • parse-namestring
  • merge-pathnames
  • make-pathname
  • pathnamep
  • pathname-host
  • pathname-device
  • pathname-directory
  • pathname-name
  • pathname-type
  • pathname-version
  • namestring
  • file-namestring
  • directory-namestring
  • host-namestring
  • enough-namestring
  • user-homedir-pathname

4.4.2 Open and Close

  • open
  • MACRO with-open-file

4.4.3 File Operation

  • rename-file
  • delete-file
  • probe-file
  • file-write-data
  • file-author
  • file-position
  • file-length
  • file-string-length
  • directory: Examining directory.

4.4.4 Other

  • load: Load a common lisp file and evaluate the forms.

5 Common Lisp Object System

5.1 TODO Concept

5.2 Functions

  • add-method
  • call-method
  • call-next-method
  • change-class
  • class-name
  • class-of
  • compute-applicable-methods
  • defclass
  • defgeneric
  • define-method-combination
  • defmethod
  • documentation
  • ensure-generic-function
  • find-class
  • find-method
  • function-keywords
  • generic-flet
  • generic-function
  • generic-labels
  • initialize-instance
  • invalid-method-error
  • make-instance
  • make-instances-obsolete
  • method-combination-error
  • method-qualifiers
  • next-method-p
  • no-applicable-method
  • no-next-method
  • print-object
  • reinitialize-instance
  • remove-method
  • shared-initialize
  • slot-boundp
  • slot-exists-p
  • slot-makunbound
  • slot-missing
  • slot-unbound
  • slot-value
  • update-instance-for-different-class
  • update-instance-for-redefined-class
  • with-accessors
  • with-added-methods
  • with-slots

6 ASDF (Another System Definition Facility)

6.1 Load ASDF

ASDF should come along with lisp implementations.

  • (require "asdf")
  • (asdf:asdf-version) to check whether it is loaded, what's the version

Alternatively, you can load the specific file by (load "/path/to/asdf.lisp")

The default load path is

  • ~/common-lisp/
  • ~/.local/share/common-lisp/source/

However, quicklisp should already configured the load path.

6.2 Load System

  • (require "asdf")
  • put package somewhere so that ASDF can find it
    • ~/common-lisp/
    • ~/.local/share/common-lisp/source/
  • load by (asdf:load-system "my-system")

Some functions:

  • load-system
  • compile-system
  • test-system
  • make
  • require-system

6.3 Build System

  • (require "asdf")
  • put your code into a new directory called my-system/ inside the findable path:
    • ~/common-lisp/
    • ~/.local/share/common-lisp/source/
  • In the directory, create a new file my-system.asd and specify dependencies
  • load by (asdf:load-system "my-system")

The system is specified using defsystem syntax. An example (hello-lisp.asd):

;; Usual Lisp comments are allowed here
(defsystem "hello-lisp"
    :description "hello-lisp: a sample Lisp system."
    :version "0.0.1"
    :author "Joe User <joe@example.com>"
    :licence "Public Domain"
    :depends-on ("optima.ppcre" "command-line-arguments")
    :components ((:file "packages")
                 (:file "macros" :depends-on ("packages"))
                 (:file "hello" :depends-on ("macros"))))

7 Appendix

7.1 Installation

7.1.1 quicklisp

;; sbcl --load /path/to/quicklisp.lisp
(load "/path/to/quicklisp.lisp")
(quicklisp-quickstart:install)

;; setting up
(load "~/quicklisp/setup.lisp")
;; load quicklisp when you start lisp
(ql:add-to-init-file)

;; install/remove a software
(ql:quickload "clx-truetype")
(ql:uninstall "clx-truetype")

;; query installed packages
(ql:system-apropos "substring")

;; updating all packages
(ql:update-all-dists)
;; update quicklisp itself
(ql:update-client)
(ql:quickload "name")
load a system
(ql:system-apropos "term")
search

A list of packages used:

clx-truetype
for stumpwm ttf-font
zpng
for stumpwm screenshot

7.1.2 packages

  • cl-quicklisp

7.1.3 org babel

first, start M-x slime, then you can evaluate this:

(princ message)

7.1.4 Slime

  • slime (emacs IDE)
  • sbcl ("lisp" executer)
  • cl-quicklisp (package manager)

In emacs: start slime

CL-USER> (load "/path/to/quicklisp.lisp")
CL-USER> ;; follow screen command to install
CL-USER> (load "~/quicklisp/setup.lisp") ;; load it

CL-USER> (ql:add-to-init-file) ;; add to sbcl's init file

CL-USER> (ql:quickload "clx-truetype") ;; download this package. Packages will be put into "~/quicklisp/xxx/dist"

CL-USER> (ql:update-all-dists) ;; update
CL-USER> (ql:update-client) ;; update quicklisp itself

The staff added into .sbclrc:

;;; The following lines added by ql:add-to-init-file:
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
7.1.4.1 Commands
command description
C-c C-d d slime-describe-symbol
C-c C-d f slime-describe-function
M-TAB slime-complete-symbol

In a buffer of mode lisp, C-c C-c will evaluate the defun around cursor. C-c C-z will switch to the slime buffer.

7.2 Practical Common Lisp

7.2.1 CD database

;; (HEBI: hello world, testing environment)
(defun hello-world ()
  (format t "Hello, world!"))

;; this function makes the cd
(defun make-cd (title artist rating ripped)
  ;; (HEBI: the list created is a property list. The :key is the key, and followed by the value)
  (list :title title :artist artist :rating rating :ripped ripped))

;; make a cd record
(make-cd "Roses" "Kathy Mattea" 7 t)

;; (HEBI: the *xx* is the convention for a global variable)
(defvar *db* nil)

;; (HEBI: The push will push the cd onto the global *db*)
(defun add-record (cd) (push cd *db*))



;; add some records to the database
(add-record (make-cd "Roses" "Kathy Mattea" 7 t))
(add-record (make-cd "Fly" "Dixie Chicks" 8 t))
(add-record (make-cd "Home" "Dixie Chicks" 9 t))


(defun dump-db ()
  ;; (HEBI: dolist)
  (dolist (cd *db*)
    ;; (HEBI: format)
    ;; the first is the output stream, with t as standard output
    ;; The ~a directive is the aesthetic directive; it means to consume one argument and output it in a human-readable form
    ;; It will work for both keyword and value
    ;; ~t is for tabulating. ~10t means emit enough spaces to move to the tenth column
    ;; ~{ and ~} will make format: 1. require the next argument to be a list 2. consume the elements of the list for each ~a inside them
    ;; ~% emit a new line
    (format t "~{~a:~10t~a~%~}~%" cd)))

;; (HEBI: note: the above function can use format to iterate the whole *db* list)
(defun dump-db-2 ()
  (format t "~{~{~a:~10t~a~%~}~%~}" *db*))


(defun prompt-read (prompt)
  ;; the *query-io* is a global variable that contains the input stream connected to the terminal
  (format *query-io* "~a: " prompt)
  ;; (HEBI: flush)
  (force-output *query-io*)
  ;; read-line will read the string without the trailing newline
  (read-line *query-io*))

(defun prompt-for-cd ()
  (make-cd
   ;; read a string
   (prompt-read "Title")
   (prompt-read "Artist")
   ;; (HEBI: parse the string to int)
   ;; if nil, the parse-integer will emit error. :junk-allowed t will make it silent
   ;; the surrounding "or" will make a default value of 0 instead of nil
   (or (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
   ;; (HEBI: y-or-n-p) is a builtin function. It is very robust, in the sense that it will reopen the prompt if answer is not yY or nN.
   (y-or-n-p "Ripped [y/n]: ")))

(defun add-cds ()
  (loop (add-record (prompt-for-cd))
     ;; this loop will end if the another query is answered as n
     (if (not (y-or-n-p "Another? [y/n]: ")) (return))))

(defun save-db (filename)
  ;; (HEBI: open the file and store the stream) as variable "out"
  ;; filename is the filename string
  ;; direction defaults to :input, so if want output, need to specify
  ;; if-exists, overwrite it
  (with-open-file (out filename
                       :direction :output
                       :if-exists :supersede)
    ;; this is used to ensures that certain variables that affect the behavior of print are set to their standard values.
    ;; be sure to use the same macro when reading the data back
    (with-standard-io-syntax
      ;; (HEBI: directly print the *db* to the stream)
      ;; lisp will print the object out in the form that it can be read back
      (print *db* out))))

;; now you can save it
(save-db "~/my-cds.db")

;; load the db back
(defun load-db (filename)
  (with-open-file (in filename)
    (with-standard-io-syntax
      ;; use read to (HEBI: read everything from the stream in)
      ;; use (HEBI: setf) to set result of the read to the *db* variable
      (setf *db* (read in)))))

;; query
(defun select-by-artist (artist)
  ;; make a copy of *db* by removing if not the predicate, and return that copy
  (remove-if-not
   ;; (HEBI: getf can get the value of a plist by the key)
   ;; #' is the quote for function
   #'(lambda (cd) (equal (getf cd :artist) artist))
   *db*))

(defun select (selector-fn)
  (remove-if-not selector-fn *db*))

(defun artist-selector (artist)
  #'(lambda (cd) (equal (getf cd :artist) artist)))

;; use this by:
(select (artist-selector "Dixie Chicks"))


;; keyword argument, can be called by (func :key value)
;; default value using (var default)
;; (var default var-p) var-p is used to check whether the argument is supplied or not
(defun where (&key title artist rating (ripped nil ripped-p))
  #'(lambda (cd)
      (and
       (if title    (equal (getf cd :title)  title)  t)
       (if artist   (equal (getf cd :artist) artist) t)
       (if rating   (equal (getf cd :rating) rating) t)
       (if ripped-p (equal (getf cd :ripped) ripped) t))))

;; use by:
(select (where :rating 10 :ripped nil))

(defun update (selector-fn &key title artist rating (ripped nil ripped-p))
  (setf *db*
        ;; (HEBI: mapcar) apply the function to each element of the list, and return the list of results
        (mapcar
         #'(lambda (row)
             (when (funcall selector-fn row)
               ;; this (setf (getf) xx) staff is magic. setf has nothing to do with getf
               (if title    (setf (getf row :title) title))
               (if artist   (setf (getf row :artist) artist))
               (if rating   (setf (getf row :rating) rating))
               (if ripped-p (setf (getf row :ripped) ripped)))
             row) *db*)))

;; this can be called:
(update (where :artist "Dixie Chicks") :rating 11)

(defun delete-rows (selector-fn)
  (setf *db* (remove-if selector-fn *db*)))

;; OK, refactoring time
;; Problems for where:
;; the if ... checking inside "and" is almosts the same, that's duplicate code
;; for the querys that do not have other fields, we don't want to check those fields, to avoid overhead

;; The solution is the MACRO, the code generator of lisp

;;; (HEBI: Macros, all kinds of quoting)
(defun make-comparison-expr (field value)
  ;; ' will leave the expression unevaluated.
  ;; ` will do the same thing, and it can do one more: can evaluate part of it
  ;; , before a subexpression will evalute that
  `(equal (getf cd ,field) ,value))

(defun make-comparisons-list (fields)
  (loop while fields
     ;; using loop facility, make comparison expr for all the fields
     ;; pop will pop the first of the list
     collecting (make-comparison-expr (pop fields) (pop fields))))

;; wrap comparison expr into and clause
(defmacro where (&rest clauses)
  ;; ,@() will evaluate the subexpression, and splice the resulting list into the surrounding list
  `#'(lambda (cd) (and ,@(make-comparisons-list clauses))))

;; this can check what this macro expanded to
(macroexpand-1 '(where :title "Give Us a Break" :ripped t))

;; Final test:
(select (where :title "Give Us a Break" :ripped t))

7.2.2 Unit Test Framework

;; the design goal of a unit test framework:

;; - easy to add new test
;; - easy to run tests
;; - easy to track down test failures


;; (HEBI: report test name)
(defmacro deftest (name parameters &body body)
  "Define a test function. Within a test function we can call
   other test functions or use 'check' to run individual test
   cases."
  `(defun ,name ,parameters
     ;; (HEBI: hierarchy test name report)
    (let ((*test-name* (append *test-name* (list ',name))))
      ,@body)))


(defmacro with-gensyms ((&rest names) &body body)
  ;; gensym generate a unique symbol name that the reader has never seen
  ;; the reason to use such unique name is to avoid leaking of information
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))

(defvar *test-name* nil)


(defmacro combine-results (&body forms)
  "Combine the results (as booleans) of evaluating 'forms' in order."
  (with-gensyms (result)
    `(let ((,result t))
      ,@(loop for f in forms collect `(unless ,f (setf ,result nil)))
      ,result)))

;; this will generate
;; (let ((result t))
;;   (unless (foo) (setf result nil))
;;   (unless (bar) (setf result nil))
;;   (unless (baz) (setf result nil))
;;   result)

(defun report-result (result form)
  "Report the results of a single test case. Called by 'check'."
  (format t "~:[FAIL~;pass~] ... ~a: ~a~%" result *test-name* form)
  result)


(defmacro check (&body forms)
  "Run each expression in 'forms' as a test case."
  `(combine-results
    ,@(loop for f in forms collect `(report-result ,f ',f))))


;; usage example:
(deftest test-+ ()
  (check
    (= (+ 1 2) 3)
    (= (+ 1 2 3) 6)
    (= (+ -1 -3) -4)))

Author: Hebi Li

Created: 2017-04-24 Mon 17:41

Validate