Common Lisp Tutorial

This is my attempt to make a Common Lisp tutorial, since all of the ones I found on the internet were either out of date or needlessly confusing.

This is a guide that I made of information I wish I had when starting with Lisp. It does assume prior programming knowledge in another language. It's hardly comprehensive, but will hopefully be a good starting place for learning the language. Enjoy!

Getting Started

In Lisp, nearly everything is a function. Even the mathematical operators. For example:

(+ (* 2 3) 1)

equals 7.

As you can tell, the functions open and close with parenthesis. The format for calling functions in Lisp is

(function arg1 arg2)

Variables and Data Types

setq is used to set variables.

(setq var 32)
(setq str "Texas")
(setq lst '(1 2 3))

The three types of data here are numbers, strings, and lists. Notice that Lisp, unlike Java, is dynamically typed.

Note: The ' in list statement is called the quote operator, and tells Lisp that the input is a list, and not to interpret it as a function.

setq sets the variable globally. To create a local variable (e.g. inside a function), use the let function.

(let
  ((a 1)
   (b 2))

  ... )

The variables a and b will only be defined within let's parentheses.

Comments

To comment in Lisp, prefix the line with ;

; This is a comment!

Conditionals

The if statement is a bit different from other programming languages.

(if (< 2 3)
  (... true ...)
  (... false ...))

Lisp executes the first block of code if the conditional statement is true. The second statement (which is optional) serves as the else statement.

If you want to execute multiple functions in the if statement (which is common) use the progn function, which serves to group multiple functions together.

(if (> 3 4)
  (progn
    (setq x (+ x 1))
    (setq y x))
  (setq x 0))

If you want an if else statement, then you'd want to use the cond function

(cond
  ((> x 1) (setq y 1)) 
  ((< x 1) (setq y 2)) 
  (t (setq y 0)))

For boolean values, t represents true, and nil represents false. Lisp treats an empty list '() (or nil) as false and all other inputs as true.

This is a convenient feature of the language to know. For instance, to do something only if a list is not empty, the following two chunks of code are identical.

(if (> (length lst) 0)
  (...))
(if lst
  (...))

While loops are accomplished in the following manner:

(loop while (> n 0)
      (setq n (- n 1)))

Although for the most part, recursion is the more popular way to accomplish loops.

Lists

Lists are important in Lisp, which is why Lisp stands for LISt Processing. I'd recommend reading up on Wikipedia's article on linked lists to fully understand this section.

(setq lst '(1 2 3))

To get the first item from the list, use the first function. To get the rest of the items, use rest. The are historically known as car and cdr, so you may see it referred to as such in older texts.

(first lst) => 1
(rest lst) => (2 3)

Lisp provides some helpful shortcuts to access other items in the list as well.

(second lst) => 2
(third lst) => 3
(fourth lst) => nil

Note: You could access these elements without these functions through repeatedly using first and rest. For instance, second is equivalent to (first (rest lst))). These functions are to save you a bit of typing.

To add an item to the beginning of the list, use the cons function. cons returns a new list with the element prefixed to the beginning of the list.

(cons 0 lst) => (0 1 2 3)

These functions really only make sense in the context of recursion, which is very prevalent in Lisp. Below is an example of a recursive sum function which uses both first and rest in a recursive context.

(defun sum (lst)
  (if (not lst)
    0
    (+ (first lst) (sum (rest lst)))))

Functions

defun is used to define functions.

(defun square (x)
  (* x x))

(defun add (x y)
  (+ x y))

Note: Lisp implicitly returns the value of the last statement in a function.

Calling functions is pretty straight forward; we've been doing it throughout this guide.

(square 9) => 81
(add 2 4) => 6

Functional Programming

One of Lisp's most powerful features is the ability to pass functions to other functions. I'll list below some of the built in functions in Lisp that take advantage of this feature and are important to know. Most of these functions take two arguments, a function and a list.

(mapcar 'square '(1 2 3 4 5)) => '(1 4 9 16 25)
(remove-if 'oddp '(1 2 3 4 5)) => '(2 4)

Note: Built-in functions in Lisp that end in a 'p' are predicates and return a boolean value.

(reduce '+ '(1 2 3 4 5)) => 15

Writing your own function to take in a functions is not particularly hard. Below is an example of how you could implement your own mapcar.

(defun my-mapcar (fn lst)
  (if lst
    (cons (funcall fn (first lst)) (my-mapcar fn (rest lst)))))

Note: funcall is used to run functions that are stored in variables

Anonymous Functions

It's occasionally useful (particularly with the higher-order functions listed above) to create a function without a name, typically because it is only getting used once.

For instance, say you wanted to double all the elements in a list. A function to double a number would rarely get used outside this call, so this is a good opportunity to create an anonymous function. The following two chunks of code are equivalent.

(defun double (x) (* x 2))

(mapcar 'double '(1 2 3 4 5)) => '(1 4 6 8 10)
(mapcar (lambda (x) (* x 2)) '(1 2 3 4 5)) => '(1 4 6 8 10)

It'll be a judgement call whether to go with the brevity of an anonymous function or the readability afforded by naming the function.

Printing

The print function can be used for basic output.

(print 512)

More complicated printing in Lisp is a bit difficult. Lisp has the format function, which is analogous to the printf function in C. The basic structure is

(format t "~a ~a of beer on the wall.~%" 99 "bottles")

The t argument means to print to the standard output, ~a says to replace with the variable, and ~% means newline.

For a more in depth look at Lisp's format function, refer to this chapter in Practical Common Lisp. It has everything you could possibly want to know and more about printing in Lisp.


by Max Timkovich (if you have any suggestions for improvements, shoot me an email)

last updated August 6, 2014