learning-lisp contains my notes on Lisp.
In Lisp a comment is started by the ;
character. Depending on where the comment is placed, the number of ;
characters vary:
- Use one semicolon for a comment at the end of a line.
- Use two semicolons for a comment that is inlined with the code.
- Use three semicolons for a comment that document code blocks and start in column 1.
- Use four semicolons for titles and introductory information on a code file.
The following sample shows how comments should be used:
;;;; This sample implements a simple hello world application.
;;; Define the hello-world function.
(defun hello-world ()
;; Print the string hello world to the console.
(princ "Hello world!")) ; Might use prin1 instead.
(hello-world) ; Finally, call the hello-world function.
Lisp contains two literals that are of type BOOLEAN
and hence are typically used in a boolean context:
t
representstrue
nil
representsfalse
Besides nil
there are a number of expressions that also evaluate to false
and are commonly referred to as equivalents to nil
within conditions:
'nil
()
'()
Please note that not all of them are literals, especially the quoted ones.
Characters are represented by using the prefix #\
. Hence, e.g. the character a
becomes #\a
, as in
(princ #\a) ; => a
Additionally, there are a few special character literals, such as:
#\newline
#\space
#\tab
To switch Lisp into data mode you have to use quoting. This can either be done by using quote
or by prefixing a list with the '
character. Hence the following two lists are equivalent:
(quote (2 3 5 7 11))
'(2 3 5 7 11)
There is also quasi-quoting which uses the `
character as a prefix, but allows to switch back to code mode using a ,
:
`(2 3 ,(+ 2 3) 7 11)
;; => (2 3 5 7 11)
To define global variables use defparameter
and defvar
. They differ in terms of repeatability: While a variable defined with defparameter
can be re-defined, variables defined with defvar
can't.
In Lisp, it is common to surround global variable names with so-called ear-muffs, that is, with a *
as prefix and suffix:
(defparameter *the-answer-to-everything* 42)
To define local variables use let
. You can define one or more variables, and they are only visible in the body of let
:
(defun add ()
(let ((x 23)
(y 42))
(+ x y)))
To assign a value to a variable use setf
:
(setf *foo* 23)
An expression may be used for the value to be assigned. Hence, the sample above may be rewritten as:
(setf *foo* (+ 10 13))
To define a global function use defun
. Specify the name of the function as well as its parameters and its body.
(defun add (x y)
(+ x y))
If a function doesn't have any parameters, simply specify an empty list.
To define local functions use flet
. Basically it works like let
, except that it defines functions instead of variables.
(defun calculate ()
(flet ((add (x y)
(+ x y))
(mul (x y)
(* x y)))
(mul 2 (add 3 4))))
(calculate)
;; => 14
Alternatively, you may use labels
. It works in exactly the same way as flet
, but defined functions can use the ones defined earlier. In contrast, when using flet
, the functions are only accessible from flet
's body.
To define a lambda function use lambda
.
(mapcar (lambda (x) (* x x)) '(1 2 3 4 5))
;; => (1 4 9 16 25)
First of all, Lisp supports the four basic arithmetic operations: +
, -
, *
and /
, that are used in prefix notation:
(* 2 (+ 3 4)) ; => 14
In contrast to other languages, the codomain is not limited, i.e. numbers may become arbitrarily large. Additionally, Lisp supports division with rational numbers, such as:
(/ 1 3) ; => 1/3
If you just want to add or subtract one from a given value, you may use 1+
and 1-
. Hence, the following two lines of code are equivalent:
(- 7 1) ; => -6
(1- 7) ; => -6
To do arithmetic shifting use ash
. It requires an expression and the number of bits to shift to the left or to the right. Positive values shift to the left, while negative values shift to the right:
(ash 16 1) ; => 32
(ash 16 3) ; => 128
(ash 16 -1) ; => 8
(ash 16 -3) ; => 2
To calculate the power of a value use expt
and provide the base as well as the exponent. In contrast, to calculate the square root use sqrt
:
(expt 2 4) ; => 16
(sqrt 16) ; => 4
In Lisp there are a number of pre-defined predicate functions. They end in p
, or - if the function itself already contains a hyphen - in -p
.
To test whether an expression is equivalent to nil
use null
:
(null ()) ; => t
(null nil) ; => t
(null #\a) ; => nil
To test whether a number is even or odd, use evenp
and oddp
:
(evenp 42) ; => t
(evenp 23) ; => nil
(oddp 42) ; => nil
(oddp 23) ; => t
To test whether a number is equal to zero use zerop
:
(zerop 0) ; => t
(zerop 1) ; => nil
To test whether a character is of a specific class of characters, use alphanumericp
and digit-char-p
:
(alphanumericp #\a) ; => t
(alphanumericp #\1) ; => t
(alphanumericp #\_) ; => nil
(digit-char-p #\a) ; => nil
(digit-char-p #\1) ; => t
(digit-char-p #\_) ; => nil
First of all, to access a function by name, you need to use function
or its short cut #'
. Hence the two following expressions are equivalent:
(function evenp)
#'evenp
To map a list to a function, use mapcar
or mapc
, depending on whether you want the mapped list to be returned or not (mapcar
does, mapc
doesn't and instead returns the original list):
(mapcar #'evenp '(1 2 3 4 5))
;; => (nil t nil t nil)
(mapc #'evenp '(1 2 3 4 5))
;; => (1 2 3 4 5)
While mapcar
rather conforms to the map
function in functional programming, mapc
is more like a for each
loop and hence only useful for potential side-effects of the specified function.
Besides that there is also maplist
which in each iteration processes the rest of the list:
(maplist #'identity '(1 2 3))
;; => ((1 2 3) (2 3) (3))
Sometimes it is necessary to get the complement of a specific function, e.g. it might be useful to have a predicate that matches everything but a digit, so this would be the complement of digit-char-p
.
To get the complement use complement
:
(mapcar (complement #'digit-char-p) '(#\a #\1 #\_))
;; => (t nil t)
To invoke a function use apply
or funcall
, depending on whether the parameters are given as a list or as separate values.
(apply #'evenp '(42)) ; => t
(apply #'evenp '(23)) ; => nil
(funcall #'evenp 42) ; => t
(funcall #'evenp 23) ; => nil
If you want to evaluate an expression lazily, move it into a lambda function. Then you can move around this function and call it when needed. If such a function does not have any parameters, it is called a nullary function or a thunk:
(defun run (thunk)
(funcall thunk))
(run (lambda () (+ 23 42)))
;; => 65
Sometimes code is given as data, e.g. when reading code from the console. To run such code use eval
:
(eval '(+ 23 42))
;; => 65
As usual, eval
should be considered bad style and be used carefully.
Lists in Lisp consist of cons cells that are linked to each other. Hence a list is, technically speaking, a single-linked list.
To create a list use cons
and specify the value of the cons cell as well as the next cons cell, or nil
if it is the last element of a list:
(cons 2 (cons 3 (cons 5 (cons 7 (cons 11 nil)))))
;; => (2 3 5 7 11)
Alternatively you may use list
:
(list 2 3 5 7 11)
;; => (2 3 5 7 11)
If you only use a single cons cell you can create a so-called dotted list respectively a pair:
(cons 2 3) ; => (2 . 3)
Additionally, you may build lists of pairs. These are called association lists (or alist):
'((1 . 1)
(2 . 4)
(3 . 9)
(4 . 16))
To get the length of a list use length
:
(length '(2 3 5 7 11)) ; => 5
To get the first item from a list use car
:
(car '(2 3 5 7 11)) ; => 2
To get the rest from a list use cdr
:
(cdr '(2 3 5 7 11)) ; => (3 5 7 11)
Now you can combine these two. E.g., to access the third item from a list you first get the rest, then the rest of the rest, and then the first item, i.e. you nest car
, cdr
and cdr
:
(car (cdr (cdr '(2 3 5 7 11)))) ; => 5
Alternatively, you can combine those into a single call by merging the a
and d
characters:
(caddr '(2 3 5 7 11)) ; => 5
This works for up to four levels.
To concatenate lists use append
and provide an arbitrary number of lists:
(append '(2 3) '(5 7 11))
;; => (2 3 5 7 11)
To add a new item to a list use push
. Please note that push
adds to the beginning of the list, not to its end:
(defparameter *primes* '(3 5 7 11))
(push 2 *primes*)
*primes* ; => (2 3 5 7 11)
To detect whether a given element is contained within a list use member
. If the element is contained, member
returns the rest of the list starting with the given element, otherwise it returns nil
:
(member 5 '(2 3 5 7 11)) ; => (5 7 11)
(member 4 '(2 3 5 7 11)) ; => nil
Please note that this also works if you are looking for nil
itself, as in the case of success member
returns a list containing nil
.
Alternatively, you may use find
. It returns the element itself if it is contained, rather than a list:
(find 5 '(2 3 5 7 11)) ; => 5
(find 4 '(2 3 5 7 11)) ; => nil
Additionally you may specify a function that is used to get the key. This is useful if you want to search an alist
for a given key:
(find 'foo '((foo . bar) (baz . bas)) :key #'car)
;; => (foo . bar)
You may also use find-if
if you want to describe the element being looked for using a function:
(find-if (lambda (p) (> p 5)) '(2 3 5 7 11))
;; => 7
Finally, when working with alist
s, you can also use assoc
to get an element by its key, instead of using find
with its :key
parameter set to #'car
:
(assoc 'foo '((foo . bar) (baz . bas)))
;; => (foo . bar)
If you want to replace elements of a list according to a given criterion use substitute-if
and provide the replacement, a predicate and the list itself:
(substitute-if 0 #'evenp '(1 2 3 4 5 6 7 8 9 10))
;; => (1 0 3 0 5 0 7 0 9 0)
Alternatively, you may want to remove the elements that match the predicate. For that use remove-if
:
(remove-if #'evenp '(1 2 3 4 5 6 7 8 9 10))
;; => (1 3 5 7 9)
To get the length of a string use length
:
(length "Hello world!") ; => 12
If you need a substring of a string use subseq
and provide the start as well as the end index. Please note that they are zero-based:
(subseq "Hello world!" 6 11)
;; => "world"
Sometimes you need to trim a string, i.e. remove one or more characters from its beginning, its end, or from both. For that use string-trim
:
(string-trim " " " Hello world! ")
;; => "Hello world!"
Please note that you can provide more than character by simply specifying them as a string in the first parameter:
(string-trim " $" "$ Hello world! ")
;; => "Hello world!"
To concatenate multiple strings into a single one use concatenate
and provide an arbitrary number of strings. Please note that you must specify the expected type of the result, i.e. 'string
:
(concatenate 'string "Hello " "world!")
;; => "Hello world!"
Sometimes you want to replace parts of a string with other text. For that you can use substitute-if
and provide an appropriate predicate function:
(substitute-if #\_ (complement #'alphanumericp) "Hello world!")
;; => "Hello_world_"
To convert characters to uppercase or lowercase, use char-upcase
and char-downcase
:
(char-upcase #\a) ; => A
(char-downcase #\A) ; => a
When comparing values Lisp provides a number of possibilities. Some of them work only for specific types, others work for a variety of types as well.
The simplest comparison possible is by using eq
. It compares two values by reference, or - to put it differently - it compares their identity. Hence eq
is primarily used to compare symbols:
(eq 'a 'a) ; => t
(eq 'a 'b) ; => nil
You may also use eql
which basically does the same as eq
, but also handles numbers and characters. Anyway, number must be of the same type, either both integer or both decimal:
(eql #\a #\a) ; => t
(eql 1.0 1.0) ; => t
There are specific ways to compare specific types. E.g., is you want to compare numbers, no matter whether they are integers or decimals, use =
:
(= 1 1) ; => t
(= 1 1.0) ; => t
Instead, if you need to compare characters use char-equal
. Please note that this ignores the casing of the given characters:
(char-equal #\a #\a) ; => t
(char-equal #\a #\A) ; => t
To compare not only single characters but complete strings use string-equal
. Please note that this also ignores the casing:
(string-equal "Hello" "Hello") ; => t
(string-equal "Hello" "HELLO") ; => t
Additionally to the comparisons mentioned before there are a number of more complex ones.
The most important one is equal
that compares two values isomorphicly, i.e., as a rule of thumb you might say that things are considered to be equal when they look the same:
(equal 'foo 'foo) ; => t
(equal 1 1) ; => t
(equal 1.0 1.0) ; => t
(equal #\a #\a) ; => t
(equal "abc" "abc") ; => t
(equal '(2 3) '(2 3)) ; => t
If you rather want to compare for common meaning than for common looks, use equalp
instead. It will also compare numbers of different types and ignore different casing when comparing characters and strings.
For numbers, just like =
, you can also use >
, <
, >=
and <=
.
To cast a type into another one use coerce
and provide the expression to cast as well as the demanded target type:
(coerce "Hello" 'list)
;; => (#\H #\e #\l #\l #\o)
(coerce (#\H #\e #\l #\l #\o) 'string)
;; => "Hello"
To merge multiple boolean values into a single you can either use and
or or
. Both provide shortcut evaluation:
(and t t) ; => t
(and t nil) ; => nil
(or t t) ; => t
(or t nil) ; => t
Please note that and
and or
also work with values of types other than BOOLEAN
. The following lines show a few examples:
(and 23 42) ; => 42
(and nil 42) ; => nil
(or 23 42) ; => 23
(or nil 42) ; => 42
To run code only when a certain condition is met use when
, provide the condition itself and one or more expressions to run:
(defun is-prime (n)
(when (find n '(2 3 5 7 11))
'n-is-prime))
To run code when a condition is not met use unless
instead:
(defun is-not-prime (n)
(unless (find n '(2 3 5 7 11))
'n-is-not-prime))
Finally, when you need when
and unless
in combination use if
. Please note that if
does not support multiple expressions, hence both code paths must consist of a single expression:
(defun is-prime (n)
(if (find n '(2 3 5 7 11))
'n-is-prime
'n-is-not-prime))
If you actually need to provide multiple expressions where only a single one is allowed use progn
:
(progn (princ "Hello ") (princ "world!"))
From time to time you need to check multiple conditions and decide which branch to use. For these cases Lisp provides cond
that takes pairs of conditions and expressions. Usually the last branch is specified by the condition t
. Hence, its canonical form is:
(cond
(<condition> <expression> <...>)
(<condition> <expression> <...>)
(t <expression> <...>))
A real-life example may be an implementation of fizz buzz:
(defun is-fizz-buzz (n)
(cond ((and (zerop (mod n 3)) (zerop (mod n 5))) 'fizz-buzz)
((zerop (mod n 3)) 'fizz)
((zerop (mod n 5)) 'buzz)
(t n)))
(is-fizz-buzz 15)
;; => fizz-buzz
Alternatively you can use case
that compares an expression against a list of values using eql
. The last branch is specified by otherwise
. Hence, its canonical form is:
(case <expression>
((<value>) <expression> <...>)
((<value>) <expression> <...>)
(otherwise <expression> <...>))
A real-life example may be a function that uses the above mentioned calculation of fizz buzz:
(defun fizz-buzz (n)
(princ (case (is-fizz-buzz n)
((fizz) "Fizz!")
((buzz) "Buzz!")
((fizz-buzz) "Fizz-Buzz!")
(otherwise n))))
(fizz-buzz 15)
;; => "Fizz-Buzz!"
Please note that case
checks against a list of values, even in a single branch. Hence you must wrap values in parentheses. Additionally, do not quote values, as otherwise '(foo)
is resolved to (quote foo)
.
To write an expression to the console use print
. This will write the expression as seen by Lisp, e.g. including the double quotes when writing a string. Additionally, the output is terminated by a #\newline
character:
(print "Hello world!")
;; => "Hello world!"
If you want to omit the line break use prin1
instead of print
:
(prin1 "Hello world!")
;; => "Hello world!"
To write the output in a more human-friendly way use princ
. This also omits the line break, but will also omit the double quotes around strings:
(princ "Hello world!")
;; => Hello world!
To write a line break to the console use fresh-line
. This will produce a line break if the current write position is not at the beginning of the line, otherwise it won't do anything:
(fresh-line)
If you don't want to write to the console, but to a string instead, use prin1-to-string
or princ-to-string
:
(princ-to-string 'foobar)
;; => "foobar"
The common base for prin1-to-string
and princ-to-string
is write-to-string
. They only differ in the presets used.
To read data from the console use read
. This will interpret everything as Lisp data, hence you need to surround strings by double quotes, e.g.:
(eval (read))
If you actually want the user to only enter a string use read-line
instead, and you will always get a string:
(eval (read-line))
Of course, as in any other language, running eval
on basically arbitrary code is a major security concern, so you should be careful with this one.
If you have Lisp code within a string that you want to turn into actual code use read-from-string
:
(eval (read-from-string "(princ \"Hello world!\")"))
To open a file use with-open-file
and provide the name of a stream, the name of the file and, optionally, some parameters to control how the file is being opened. Inside the body of with-open-file
you can then use the stream's name as variable to write to:
(with-open-file (my-stream "foo.txt" :direction :output :if-exists :supersede)
(princ "Hello world!" my-stream))
If you specify *standard-output*
as stream name, any writes to the standard output become redirected to the file.