The Common Lisp Cheat Sheet
- The Basics
- Logic & Equality
- Local Variables & Functions
- More on Functions
- More on Lists
- More on Sequences
- Data Structures
We can define global variables with
DEFPARAMETER will always bind the supplied value, while
DEFVAR will only bind a value to a variable if no binding exists.
;; Establish an unbound variable (defvar *x*) ;; Assign the value 15 to X (defparameter *x* 15) ;; Does nothing as X already bound (defvar *x* 10)
We can define global constants with
(defconstant +my-constant+ 20)
Global functions are defined with
DEFUN using the below syntax.
(defun function-name (parameter*) "Optional documentation string." body-form*)
Below is example of a function that multiplies the sum of two numbers by 10.
(defun multiply-sum-by-10 (x y) "Returns the sum of two numbers multiplied by 10" (* 10 (+ x y))) ;; Returns 150 (multiply-sum-by-10 5 10)
SETF is LISP's general purpose assignment operator. It is a powerful setter that can assign values to many different types of places.
The syntax of
SETF is as follows. Places are typically either symbols, representing variables or functions, or getter forms that access particular places in objects that we wish to modify.
(setf place value)
Below are some examples.
;; Set x to 10 (setf x 10) ;; Set x to 1 and y to 2 (setf x 1 y 2) ;; Example of using a getter with setf (defvar *list* '(1 2 3 4)) (setf (car *list*) 5) ;; Returns (5 2 3 4) *list*
Input & Output
The LISP reader consists of three primary functions
READis used to parse input into Lisp objects and reads exactly one expression, regardless of newlines
READ-LINEreads all characters up to a newline, returning a string
READ-FROM-STRINGtakes a string and returns the first expression from it
Below are some examples you can experiment with.
(defparameter my-variable nil) (setf my-variable (read-line)) (setf my-variable (read)) (setf my-variable (read-from-string "(1 2 3)"))
The most useful LISP printing functions are:
PRIN1are used to generate output useful for programs (the former adds a newline to the output whilst the latter does not)
PRINCis used for output for people
FORMATis a highly configurable printer and the most commonly used printer
The syntax of
FORMAT is as follows:
(format destination control-string optional-arguments*)
The first argument of the
FORMAT function is the destination where the output will be printed.
- A value of
Twill send the out to the stream
*STANDARD-OUTPUT*(typically the main screen of your LISP system) whilst
NILhere will return the output as a string. We can also supply a stream here
- The second argument is the string that we want to print. However, we can enter directives (preceded by
~) to add complex behaviour to the string, such as
~%to print newlines
~Ato print LISP objects for humans
~Sto print LISP objects for computers, i.e. suitable as as input for the LISP reader
The third (optional) argument of the
FORMAT function is the arguments we want to supply to the control string. Each
~S in the control-string takes a successive argument from here and places it into the string.
This is best illustrated by the following examples. Note how Bob is quoted in the second example, as the printed representation of LISP strings includes quotes.
;; Prints to screen: Dear Bob, How are you? (format t "Dear ~A, ~% How are you?" "Bob") ;; Prints to screen: Dear "Bob", How are you? (format t "Dear ~S, How are you?" "Bob") ;; Returns "Number is: 3" (a string) (format nil "~A ~A" "Number is:" (+ 1 2))
Basic numerical functions include
/. They can take more than two operands.
;; Returns 20 (+ 4 7 9)
Numerical comparisons can be achieved with
/= (not equal to),
<=. With three or more arguments, these functions act as range checks.
For example, the below returns true as X is between 0 and 5 inclusive.
(defparameter x 5) (<= 0 x 5)
The below returns false as X > Y:
(defparameter y 4) (< 0 x y 6)
The below returns true as Y < X < 6:
(< 0 y x 6)
Other useful numerical functions are below.
;; Returns e^3 (exp 3) ;; Returns 4^5 (expt 4 5) ;; Returns log of 8 in the base 2, i.e. 3 (log 8 2) ;; Returns square root of 9, i.e. 3 (sqrt 9) ;; Returns 5 (max 1 3 5 4 2) ;; Returns -1 (min 1 -1 2 3 4) ;; Returns 3 (abs -3)
More details on numerical operations can be found in Common Lisp, the Language 2nd Edition.
CONCATENATE is a useful function to join strings. Try the following:
(concatenate 'string "Hello, " "world" ". Today is good.")
LENGTH returns the number of characters in a string. The below will return 6:
SEARCH will search for a substring within a string. The below will return 4, the starting position of the first string within the second string:
(search "term" "the term is search within this string")
Below are comparison functions for strings. Replace STRING with CHAR in the below to get the equivalent character comparison function.
|Case Sensitive||Case Insensitive|
Logic & Equality
Common Lisp has a type hierarchy and LISP objects can belong to multiple data types. One can test whether an object is of a particular type with
TYPEP. The below will return true:
(typep "My String" 'string)
Other type predicate functions include ATOM, NULL, ZEROP, NUMBERP, EVENP, LISTP, ARRAYP, PLUSP, CHARACTERP, ODDP, SYMBOLP, PACKAGEP, MINUSP, STRINGP and ODDP.
Boolean & Logic
Falsity is represted by
NIL and all other values represent truth.
NIL if any of its arguments are false and returns the value of the last argument if all arguments are true. The below will return 5 as all the arguments are true:
(and t (+ 1 2) (* 1 5))
OR returns the first argument that is true and
NIL if no argument is true. The below returns 3, the first non-nil value in the form:
(or nil (+ 1 2) (* 1 5))
Common Lisp has a few different functions for testing equality of two objects. Generally speaking, you can't go wrong with
EQcompares equality of memory addresses and is the fastest test. It is useful to compare symbols quickly and to test whether two cons cells are physically the same object. It should not be used to compare numbers
EQexcept that it can safely compare numbers for numerical equality and type equality. It is the default equality test in many Common Lisp functions
EQUALis a general purpose test that, in addition to being able to safely compare numbers like
EQL, can safely compare lists on an element by element basis. Lists are not unique and
EQLwill fail to return equality on equivalent lists if they are stored in different memory addresses
EQUALPis a more liberal version of
EQUAL. It ignores case distinctions in strings, among other things
=is the most efficient way to compare numbers, and the only way to compare numbers of disparate types, such as 3 and 3.0. It only accepts numbers
PROGN form allows multiple forms to be evaluated and the value of the last is returned as the value of the
PROGN. The below will print "Hello" and "World" and return 10:
(progn (print "Hello") (print "World") (+ 5 5))
BLOCK special operator is similar, but it is named and has a mechanism for out-of-order exit with the
- The value of the last expression in a block is returned as the value of the block (unless there is an early exit by
- All other expressions in the block are thus only useful for their side effects
The below constructs a block named MY-BLOCK and returns 10 as it returns from the block when evaluating the
(block my-block (print "We see this") (return-from my-block 10) (print "We will never see this"))
RETURN macro returns its argument as the value of an enclosing
NIL. Many Common Lisp operators that take a body of expressions implicitly enclose the body in a
NIL and we can use
RETURN in these forms:
(dolist (i '(1 2 3 5 6 7)) (if (= i 3) (return 10)) (print i))
The five main conditionals in Common Lisp are
IF form (note there is no implicit
(if (equal 5 (+ 1 4)) (print "This is true") (print "This if false"))
WHEN form (note there is an implicit
(when (equal 5 (+ 1 4)) (print "Print if statement is true") (print "Print this also"))
UNLESS form (note there is an implicit
(unless (equal 3 (+ 1 4)) (print "Only print if condition is false") (print "Print this also"))
COND form (multiple ifs, implicit
PROGN). The form exits on the first true:
(cond ((equal 5 3) (print "This will not print")) ((equal 5 5) (print "This will print")) ((equal 5 5) (print "This will not print as the") (print "form exited at first true")))
Example of a
CASE form (multiple ifs on one variable, implicit
PROGN). Cases are literal and not evaluated. The form exits on the first true:
(case (read) (1 (format t "~% Monday")) (2 (format t "~% Tuesday")) (3 (format t "~% Wednesday")) (4 (format t "~% Thursday")) (5 (format t "~% Friday")) (6 (format t "~% Saturday")) (7 (format t "~% Sunday")) (otherwise "Not an integer between 1 and 7"))
DOLIST will iterate over the items of a list and execute the loop body for each item of the list.
(dolist (my-variable my-list optional-result-form) body-form*)
Below is an example that will print the numbers 1 to 7 iteratively:
(dolist (i '(1 2 3 5 6 7)) (print i))
DOTIMES will iterate from 0 to one less than the end number supplied. If an optional result form is supplied, it will be evaluated at the end of the loop.
(dotimes (my-variable end-number optional-result-form) body-form*)
Below is an example that will print the numbers 0 to 4:
(dotimes (i 5 T) (print i))
Below is the syntax of the
(do ((var1 init1 step1) ... (varn initn stepn)) (end-test result-forms*) body-forms*)
The below example will return 81 and print 1, 0, 1, 4, 9, 16, 25, 36, 49 and 64 on newlines. During each iteration, LOOP-STEP is increased by one and SQUARE is set to the square of LOOP-STEP:
(do ((loop-step 0 (+ loop-step 1)) (square 1 (* loop-step loop-step))) ((= 10 loop-step) square) ; Stop at 10 (print square)) ; Print square at each step
Below are examples of the
LOOP macro, some from Peter D. Karp's Guide. The first will return a list of the doubles of each number in the original list:
(defvar my-list-1 '(1 2 3 4 5 6)) ;; Returns (2 4 6 8 10 12) (loop for x in my-list-1 collect (+ x x))
The below will print each of the numbers in the list iteratively:
(loop for x in my-list-1 do (print x))
The below will only collect even numbers:
(loop for x in my-list-1 if (evenp x) collect x)
The below is an example of iterating across two lists, stopping the loop at the end of the shorter list:
(defvar my-list-2 '(a b c d e)) (loop for x in my-list-1 for y in my-list-2 do (format t "X: ~a, Y: ~a, " x y))
We can also do simple loops with a counter:
(loop for x from 1 to 5 do (print x))
Below is an example of how we can use the
LOOP macro to check whether a certain predicate is true at some point within a list:
(loop for x in '(abc 2) thereis (numberp x))
We can also check whether a predicate is never true within a loop:
(loop for x in '(abc 2) never (numberp x))
We can also check whether a predicate is always true within a loop:
(loop for x in '(abc 2) always (numberp x))
Below is an example of terminating a loop early:
(loop for x from 1 for y = (* x 10) while (< y 100) do (print (* x 5)) collect y)
Finally, a few more examples illustrating the versatility of the
(loop for x in '(a b c d e 1 2 3 4) until (numberp x) do collect (list x 'abc)) (loop for x in '(a b c d e) for y from 1 when (> y 1) do (format t ", ") do (format t "~A" x)) (loop for x in '(a b c d e) for y from 1 if (> y 1) do (format t ", ~A" x) else do (format t "~A" x))
Local Variables & Functions
LET* are special operators that allow us to create local variables that can only be accessed within their closures.
LET binds its variables in parallel such that you cannot refer to another variable in the
LET form when setting the value of another.
LET* binds its variables in sequentially, so that you can refer to the value of any previously bound variables. This is useful when you want to assign names to several intermediate steps in a long computation.
LET form has the following syntax:
(let ((var-1 value-1) ... (var-n value-n)) body-form*)
Below is an example of
LET* that will return 10:
;; Prints 10 (let* ((x 5) (y (+ x x))) (print y))
Functions named by
DEFUN are global functions that can be accessed anywhere. We can define local functions
LABELS, which are only accessible within their context. The syntax of
(labels ((fn-1 args-1 body-1) ... (fn-n args-n body-n)) body-form*)
Functions defined within
LABELS take a similar format to a
DEFUN form. Within the body of the
LABELS form, function names matching those defined by the
LABELS refer to the locally defined functions rather than any global functions with the same names.
Below is an example of a
LABELS form that will return 12, the result of (+ 2 4 6), where 2, 4 and 6 are the results of evaluating the three local functions defined in the form.
(labels ((first-function (x) (+ x x)) (second-function (y) (* y y)) (third-function (z) (first-function z))) (+ (first-function 1) (second-function 2) (third-function 3)))
More on Functions
Lambda expressions allow us to create unnamed functions. These are useful when writing small functions for certain tasks. Below is an example that returns 101:
((lambda (x) (+ x 100)) 1)
By default, a function call must supply values for all parameters that feature in the function definition. We can modify this behaviour with the
&optional token allows us to distinguish between required parameters, placed before the
&optional token, and optional parameters, placed after the token:
(defun make-a-list (a b c d &optional e f g) (list a b c d e f g)) ;; Returns (1 2 3 4 5 NIL NIL) (make-a-list 1 2 3 4 5)
One drawback of the
&optional token, using the above as an example, is that we need to supply values for E and F if we want to supply the value for G, as arguments in a function call are assigned to the parameters in order.
To overcome this, we utilise the
&key token to be able to specify which optional parameter we want to assign a value to. Below is an example of this.
(defun make-a-list-2 (a b c d &key (e 1) f g) (list a b c d e f g)) ;; Returns (1 2 3 4 1 NIL 7) (make-a-list-2 1 2 3 4 :g 7)
&key is preferable to
&optional as it allows us to have greater control in our function calls. It also makes code easier to maintain and evolve as we can add new parameters to a function without affecting existing function calls (useful when writing libraries that are already dependencies for other programs).
&rest token, placed before the last variable in a parameter list, allows us to write functions that can accept an unknown number of arguments. The last variable will be set to a list of all the remaining arguments supplied by the function call:
(defun make-a-list-3 (a b c d &rest e) (list a b c d e)) (make-a-list-3 1 2 3 4 5 6 7 8) ; (1 2 3 4 (5 6 7 8))
We can utilise multiple tokens in the same function call, as long as we declare them in order:
- First the names of required parameters are declared
- Then the
- Then the
- Finally the
&keywordparameters are declared
VALUES function returns multiple values and can be used as the last expression in the body of a function. The below example returns 1, NIL and 6 (individually, not as a list):
(values 1 nil (+ 2 4))
VALUES function is supplied as an argument to a form which is only expecting one value, the first value returned by the
VALUES function is used and the rest are discarded:
;; Returns 6 (+ 5 (values 1 nil (+ 2 4)))
MULTIPLE-VALUE-BIND macro is used to receive multiple values. The first argument of this macro is a list of parameters and the second is an expression whose values are bound to the parameters. We can then use these bindings in the body of the
Below is an example that returns (1 2 3):
(multiple-value-bind (x y z) (values 1 2 3) (list x y z))
If there are more variables than values, the leftover variables will be bound to NIL. If there are more values than variables, the extra values will be discarded.
Apply & Funcall
Functions in LISP are first-class objects that generally support all operations available to other data objects, such as being modified, passed as an argument, returned from a function and being assigned to a variable.
FUNCTION special operator (shorthand
#') returns the function object associated with the name of function that is supplied as an argument:
(function +) ;; Equivalent syntax #'+
APPLY takes a function and a list of arguments for it and returns the result of applying the function to its arguments.
Below is an example that returns 6. Note how we have to use to sharp-quote
#' to pass the
+ function as an object into the
APPLY function. Without doing so, LISP will return an error as it will try to evaluate
+, which is not legally permissible in the below example.
(apply #'+ '(1 2 3))
The below is an example that illustrates the use of lambda expressions:
(apply #'(lambda (a b c) (+ a b c)) '(1 2 3))
FUNCALL is similar to
APPLY, but allows us to pass arguments individually and not as a list. Below is an example that returns 6:
(funcall #'+ 1 2 3)
Mapping is a type of iteration in which a function is successively applied to pieces of one or more sequences.
MAPCAR operates on successive elements of lists and returns a list of the result of the successive calls to the function specified. The below example will return (-1 -2 -3):
(mapcar #'(lambda (x) (- 0 x)) '(1 2 3))
MAPLIST operates on successive CDRs of the lists. The below example will return ((A B C D) (B C D) (C D) (D)):
(maplist #'(lambda (x) x) '(a b c d))
The above only work for lists. To map over other types of sequences, one can use
(map result-type function &rest sequences)
Below are a couple of examples.
;; Returns a list ((#\a #\a) (#\b #\b) (#\c #\c)) (map 'list #'(lambda (x) (list x x)) "abc") ;; Returns "1010" (map 'string #'(lambda (x) (if (oddp x) #\1 #\0)) '(1 2 3 4))
More on Lists
NTH can be used to access a particular element of a list. The below returns D, the fourth element of the list:
(nth 3 '(a b c d e f g))
NTHCDR is a similar version to access a particular CDR of a list. The below returns (D E F G):
(nthcdr 3 '(a b c d e f g))
We can also use
TENTH to access the first ten elements of a list. The below returns A:
(first '(a b c d e f g))
LAST is a very useful function to access the last CDR of a list. The below returns (G):
(last '(a b c d e f g))
Lists can be thought of as sets and a number of useful functions exist to perform set operations.
MEMBER and its variants are useful functions to test whether certain elements exist within a list. The below returns (B C), the CDR from the position B is found:
(member 'b '(a b c))
The below returns (3 4), the odd numbers in the list:
(member-if #'oddp '(2 3 4))
We can also specify the test to apply (the default is
(member 'b '(a b c) :test #'equal)
ADJOIN joins an object onto a list only if it is not already a member. The below returns (A B C) as B is already a member of the supplied list (A B C):
(adjoin 'b '(a b c))
The below returns (Z A B C) as Z is not a member of the supplied list:
(adjoin 'z '(a b c))
Set union, intersection and complement operations can be done with
The below example returns (A B C S), the union of the two lists supplied:
(union '(a b c) '(c b s))
The below examples returns (C B), the interesection of the two lists supplied:
(intersection '(a b c) '(c b s))
The below returns (A), a list of the elements in the first list supplied that do not exist in the second list:
(set-difference '(a b c) '(c b s))
Finally, the function
REDUCE is useful to extend functions that only take two variables. It takes two arguments, a function (which must take exactly two values) and a sequence.
The function is initially called on the first two elements of the sequence, and thereafter with each successive element as the second argument
The value returned by the last call is the value returned by the
For example, the below returns (A), the intersection of these three lists:
(reduce #'intersection '((b r a d) (b a d) (c a t)))
Push, Pop & Reverse
PUSH can be used to push an element to the front of a list, while the macro
POP can remove and return the first element of the list. Both are destructive operations as they directly change the original lists in question.
The below is an example of pushing 1 to the front of *MY-LIST*:
(defparameter *my-list* '(2 3 4)) (push 1 *my-list*) ;; Returns (1 2 3 4) *my-list*
The below is an example that returns the first item of *MY-LIST* and removes it from the list as well:
;; Returns 1, the car of *my-list* (pop *my-list*) ;; Returns (2 3 4) *my-list*
REVERSE is a very useful function to reverse the order of elements within a list and is frequently used in various scenarios.
Below is an example that returns (F E D C B A):
(reverse '(a b c d e f))
Association lists are a very useful data structure for mapping values to keys. They are lists of pairs (i.e. conses), with the key being the
CAR of the pair and the datum being the
CDR of the pair.
Below is an example of an association list constructed using dot pair notation:
(defvar my-a-list '((one . 1) (two . 2)))
Below is an example of returning a new association list by adding an entry to the front of a pre-existing association list:
;; Returns ((THREE . 3) (ONE . 1) (TWO . 2)) (acons 'three 3 my-a-list)
Below is an example of creating an association list from lists of keys & datums:
(pairlis '(one two three) '(1 2 3))
We can use
ASSOC to retrieve the pair associated with a key. The below returns (ONE . 1):
(assoc 'one my-a-list)
We can use
RASSOC to retrieve the pair associated with a datum. The below returns (TWO . 2):
(rassoc 2 my-a-list :test #'=)
More on Sequences
Sequences are a data type in Lisp and lists, strings and arrays are all of type sequence.
MAKE-ARRAY creates arrays. For example, we can create a 2 x 3 array as follows:
(defparameter my-array (make-array '(2 3) :initial-element nil))
We can use
SETF to access elements and set their values. The below will return NIL as the value of this element has not been set yet:
(aref my-array 0 0)
The below will set the value of this element to B, which will be the value returned by the second form below:
(setf (aref my-array 0 0) 'b) (aref my-array 0 0)
:INITIAL-ELEMENT to set the value of every element of an array to the provided argument. In the below, every element has a value of 2.
(setf my-array (make-array '(2 3) :initial-element '((1 2 3) (1 2 3))))
ARRAY-DIMENSION retrieve the the number of dimensions and the number of elements in a given dimension respectively:
;; Returns 2 (array-rank my-array)) ;; Returns 2 (array-dimension my-array 0) ;; Returns 3 (array-dimension my-array 1)
:INITIAL-CONTENTS is used to set the array to an object provided.
(defparameter my-vector (make-array 3 :initial-contents '("a" 'b 3)))
A one-dimensional array is also known as a vector and the above example created one. Vectors can also be created with
(vector "a" 'b 3)
The most famous vectors in LISP are strings. Strings are specialised vectors whose elements are characters.
Sequences have many useful functions. We can use
LENGTH to return the number of items in a sequence. The below returns 6:
(length '(a b c d e f))
REMOVE and its variants are very handy filter functions. The below returns (C R T) as a new list:
(remove 'a '(c a r a t))
The below returns "cdbra", preserving only the last duplicate of each item:
The below returns (2 4 4) by removing all odd numbers:
(remove-if #'oddp '(1 2 3 4 4))
SUBSEQ can extract a portion of a sequence. Its arguments are a list, the starting position and an optional ending position (which is not to be included in the subsequence). The below will return (B C D):
(subseq '(a b c d e f) 1 4)
SORT takes a sequence and a comparison function of two arguments and destructively (i.e. by modifying the original sequence) returns a sequence sorted according to the function. The below returns (6 5 4 2 1) by sorting in descending order:
(sort (list 1 4 2 5 6) #'>)
SOME test whether a sequence satisfies a provided predicate. The below will return NIL as not every item is odd:
(every #'oddp '( 1 2 5))
The below will return T as some of the items are odd:
(some #'oddp '( 1 2 5))
The below will return T as the items of the first sequence are greater than those of the second sequence in an element-wise comparison:
(every #'> '(1 3 5) '(0 2 4))
We can find elements within a sequence with
FIND, which returns the leftmost such element, or
POSITION, which returns the position of such an item, as an integer. The below returns 1, the item we are searching for:
(find 1 '(1 2 3 4))
The below returns 0, the position of the item we are searching for:
(position 1 '(1 2 3 4))
We can use
COUNT to count the number of instances of the element within the sequence. The below returns 3, the number of instances of 1 in the list:
(count 1 '(1 2 3 1 1 4))
We can use
SEARCH to search for sequence within another. The below returns 4, the position of the string "Hello" in "Hi! Hello, World!":
(search "Hello" "Hi! Hello, World!")
Many list and sequence functions take one or more keyword arguments from the below table. For example, we can use
POSITION to return the position of an element within a sequenc (or NIL if not found) and use keyword arguments to determine where to begin the search.
The below will return 4:
(position #\a "fantasia" :start 3 :end 7)
|:key||A function to apply to each element||identity|
|:test||The test function for comparison||eql|
|:from-end||If true, work backwards||nil|
|:start||Position at which to start||0|
|:end||Position, if any, at which to stop||nil|
A hash table is a way of associating pairs of objects, like a dictionary. The objects stored in a hash table or used as keys can be of any type. We can make hastables with
MAKE-HASH-TABLE and retrieve values associated with a given key with
(defparameter my-hash-table (make-hash-table)) ;; Returns NIL as not yet set (gethash 'color my-hash-table)
Similar to other structures, we use
SETF to set values. Hash tables can accommodate any number of elements, because they are expanded when they run out of space.
(setf (gethash 'color my-hash-table) 'red)
MAPHASH allows you to iterate over all entries in the hash table.
- Its first argument must be a function which accepts two arguments, the key and the value of each entry
- Note that due to the nature of hash tables you can't control the order in which the entries are provided to
MAPHASH(or other traversing constructs)
Below is an example, which will return COLOR = RED as there is only one item (COLOR) currently in our hash table.
(maphash #'(lambda (key value) (format t "~A = ~A~%" key value)) my-hash-table)
You can remove items from a hash table with
(remhash 'color my-hash-table)
Common Lisp provides the
DEFSTRUCT facility for creating data structures with named components. This makes it easier to manipulate custom data objects as we can refer to their components by name.
Constructor, access and assignment constructs are automatically defined when a data type is defined through
DEFSTRUCT. Consider the below example of defining a data type for rectangles.
DEFSTRUCT defines RECTANGLE to be a structure with two fields, HEIGHT and WIDTH. The symbol RECTANGLE becomes the name of a data type and each rectangle will be of type RECTANGLE, then STRUCTURE, then ATOM and then T.
DEFSTRUCT will generate four associated functions:
RECTANGLE-HEIGHT and RECTANGLE-WIDTH to access elements of the structure
RECTANGLE-P to test whether an object is of type RECTANGLE
MAKE-RECTANGLE to create rectangles
COPY-RECTANGLE to create copies of rectangles
Below is an example of the above structure.
;; Height will default to NIL while width will default to 5 (defstruct rectangle (height) (width 5))
The below creates an instance of RECTANGLE:
(defvar rectangle-1) (setf rectangle-1 (make-rectangle :height 10 :width 15))
The below will return 10:
The below will set RECTANGLE-WIDTH of RECTANGLE-1 to 20:
(setf (rectangle-width rectangle-1) 20)
Common Lisp Object System (CLOS)
Below is an example of creating two classes, one which inherits from the other. Courtesy of the Common Lisp Cookbook.
;; Define a class: (defclass person () ((name :initarg :name :accessor name) (lisper :initform "Yes" :accessor lisper))) ;; Create an instance of this class: (defvar person-1 (make-instance 'person :name "David" ))
Accessor functions are used for both getting and setting. The below will return DAVID.
The below will set name to TOM.
(setf (name person-1) "Tom")
:INITFORM is used to set default values. The below returns "Yes" (the default value) as the value of LISPER was not yet set.
The below is an example of inheriting from the PERSON class.
(defclass child (person) ((can-walk-p :initarg :can-walk-p :initform "No" :accessor can-walk-p)))
Inherited classes inherit the slots of their parents. CHILD will inherit LISPER from PERSON. The below will return "Yes":
(lisper (make-instance 'child :name "Phoebe"))
Inherited classes can also introduce new slots. CHILD introduces CAN-WLAK-P. The below will return "No":
(can-walk-p (make-instance 'child))
We can add methods to classes with a combination of
DEFMETHOD. Note that Common Lisp supports multiple dispatch so that many classes can share and use the same method names.
DEFGENERIC establishes an entry in the method dispatch table, while
DEFMETHOD allows us to create specialised versions.
;; Version with a default method (to be used if no other specialisations exist: (defgeneric greet (obj) (:documentation "Says hi") (:method (obj) (format t "Hi"))) ;; Version without default method: (defgeneric greet-2 (obj1 obj2) (:documentation "Says hi"))
In creating specialised methods, we add the parameter type to the parameter list. In a method call, LISP will then use the method which matches the parameter types of the parameters supplied in the method call.
In the below, GUEST-NAME is a parameter of type PERSON, while MESSAGE is a parameter that is not specialised and can be anything.
(defmethod greet-2 ((guest-name person) message) (format t "The person greets ~A and says ~A" guest-name message))
Finally, it is useful to create custom print output for CLOS objects. This can be achieved with the following.
(defmethod print-object ((obj person) stream) (print-unreadable-object (obj stream :type t) (format stream "~a" (name obj)))) ;; Returns #<Person Tom> (print person-1)
Reading & Writing to Files
WITH-OPEN-FILE macro is used to read and write to files. Below is an example of opening a file and then reading from it. The
NIL in the below inhibits end of file errors.
(with-open-file (my-stream "/Users/ashokkhanna/test.txt") (format t "~a~%" (read-line my-stream nil)))
Below is an example of opening a file and then writing to it.
(with-open-file (my-stream "/Users/ashokkhanna/test.txt" :direction :output :if-exists :append) (format my-stream "~a~%" "Hello, World!"))
The following open arguments can be supplied to the
|:direction :output||Write to a file insead of reading|
|:if-does-not-exist :create||Create a file if it does not exist|
|:if-exists :supersede||Replace the file that exists|
|:if-exists :overwrite||Overwrite file|
|:if-exists :append||Write to end of file|
Packages are a central mechanism for avoiding name collisions that occur if multiple files contain variables or functions with the same name. More information on packages can be found on my guide on Medium.
Packages need to be registered before they can be used. Below is an example of registering a package that inherits from two packages and exports two symbols. This example also shadows two symbols, allowing us to use, within MY-PACKAGE, the definitions of these symbols (RESTART and CONDITION in our case) that exist within the package MY-PACKAGE and not definitions of these symbols inherited from other packages (CL in our case, where RESTART and CONDITION are interned also).
(defpackage :my-package (:use :cl :other-package-1) (:export :symbol-1 :restart) (:shadow :restart :condition))
Once a package is registered with the above, we can switch to it with
Within a package, all symbols defined in that package are accessible. In addition, any exported symbols from packages inherited via the
:USEcommand can be directly accessed without a package qualifier
Outside of a package, internal symbols can be accessed via a double-colon package qualifier, e.g.
my-package::symbol-3, while exported symbols can be accessed via a single-colon package qualifier, e.g.
It is good practice to put the above at the top of LISP files so that readers can easily follow which package is currently live in a file.
Finally, note in the above, that we will get an error if we try to inherit from both CL and MY-PACKAGE due to to the clash in symbols RESTART and CONDITION that appear in both packages. To overcome this, we can use a
:shading-import-from: command, such as in the following.
(defpackage :my-package-2 (:use :cl :my-package) (:shadowing-import-from :my-package :restart))