Emacs & Lisp
Emacs
- Emacs
- Emacs config: auc-emacs-config
- start
- stop
C-x C-c - save
C-x C-s - Find the
metakey - The Emacs tutorial
C-h t - The Emacs manual
C-h r - A nice blog posting on one person's experience learning Emacs
- An Emacs Tutorial: Beginner’s Guide to Emacs
- Other resources on Learning Emacs
- Using Emacs and Learning Elisp Series if you like following YouTube videos (I have not viewed them)
- magit is an Emacs interface to git
The Common Lisp language
Data types
- Numbers
- fixnum, integer, float, long float, binary, octal, hex, any radix
- Strings
- "this is a string"
- Escape using backslash as usual
- There are no single-quoted strings or triple-quoted strings as in Python.
- Symbols
A Lisp type that Python does not have is symbol. You can consider a symbol to be a word or a tag. To write a symbol, begin with a quote
'. Note that Lisp converts a symbol to upper case (by default) and that it is case insensitive.CL-USER> 'landmark LANDMARK CL-USER> 'aBcDeFg ABCDEFG CL-USER>
- Other types
Syntax
Lisp has a uniform, fully-parenthesized, prefix syntax. Where you might write
gcd(12, 15) in Python, you write (gcd 12 15) in Lisp. Note the two
differences:
- Python uses a comma as a separator. Lisp uses whitespace.
- The Python operator is before the opening parenthesis. The Lisp operator is after it.
This may seem unfamiliar since the Python syntax is more similar to the syntax
of arithmetic you have known since elementary school. Another aspect of Lisp
syntax that may be unfamiliar is that it is completely uniform, even for
arithmetic operators, so the Python expression (5 + 4) * (3 - 2) is written
in Lisp as (* (+ 5 4) (- 3 2)). This takes a little getting used to but it
does have a number of advantages.
One is that some functions can have variable arity.
CL-USER> (+ 3 4) 7 CL-USER> (+ 3 4 5) 12 CL-USER> (+ 3 4 5 6 7 8) 33 CL-USER> (+) 0 CL-USER>
Another is that we can more easily manipulate Lisp source code, both manually and automatically, in order to perform syntax transformation. This is much more powerful than it might seem at first sight. We will return to it later.
Evaluation
To evaluate an expression, Lisp first evaluates the operator of the expression. If the operator is a function it then recursively evaluates all of its arguments from left to right. The function is then applied to the values resulting from the evaluation of the arguments.
For example, the evaluate of (+ (* 2 3) (* 4 5)) proceeds as follows:
- The operator
+is the addition function - Evaluate
(* 2 3).*is a function and2, and3are already values. Carry out the multiplication to get the value6 - Evaluate
(* 4 5). Similarly to the previous step we get20. - Add
6and20to return26.
This is the Common Lisp Evaluation Rule for functions but note that not every operator is a function. Later we will see that expressions which have some operators, known as special forms or macros, evaluate differently.
Lists
Lisp gets its name from being a LISt Processing language. We can make a list
using the list function. Just as any other function, it first evaluates its
arguments. Then it makes a list of those values.
CL-USER> (list (+ 3 4) (* 5 6) "seven") (7 30 "seven")
We see that a list is written just like a function call, with parentheses as
delimiters and whitespace as separator. Compare this with Python's notation
[7, 30, "seven"]. Also note that the list can be heterogeneous, it can
contain values of different types. Indeed a list can contain another list.
CL-USER> (list (+ 3 4) (* 5 6) (list "seven" "eight")) (7 30 ("seven" "eight"))
We will often see this kind of list structure in Lisp.
Earlier we used a quote ' to write a symbol. We can also use it to write list
structure.
CL-USER> (list 'a 'b 'c) (A B C) CL-USER> '(a b c) (A B C)
In both cases this is a list containing three symbols. In the second case the contents of the list remain unevaluated. We can see this more clearly in
CL-USER> '(a b (+ 3 4)) (A B (+ 3 4))
Nothing after the quote is evaluated right up to the matching right parenthesis. Indeed the above is equivalent to:
CL-USER> (list 'a 'b (list '+ 3 4)) (A B (+ 3 4))
Also note the distinction between the quoted plus, which is a symbol, and a bare plus which would be a function operator in that position.
t and nil
We write the empty list as a matching pair of parentheses with nothing between them.
CL-USER> ()
NIL
Note how Lisp writes this as NIL. Indeed nil evaluates to NIL
CL-USER> nil NIL
In Lisp nil does double duty as the empty list and the false value.
Truthiness and Falsiness
Wherever Lisp expects a Boolean value, for example as the first argument of an
if, it treats nil as false and any other value as true. Note how this
behaviour differs from Python where all zero and empty values are treated as
false, e.g. 0, 0.0, [], {}, etc.
Lisp has a full set of Boolean operators
(and (or nil 45) (or (not nil) "maybe"))
Variables
We can store values in variables using setf as follows
CL-USER> (setf days-in-a-week (+ 3 4)) ; in: SETF DAYS-IN-A-WEEK ; (SETF DAYS-IN-A-WEEK (+ 3 4)) ; ; caught WARNING: ; undefined variable: COMMON-LISP-USER::DAYS-IN-A-WEEK ; ; compilation unit finished ; Undefined variable: ; DAYS-IN-A-WEEK ; caught 1 WARNING condition 7 CL-USER>
Note how sbcl gives us a warning before returning the value stored. The
warning is telling us that it's good practice to define a variable before
setting it. We'll just ignore it for now and get back to that later.
Now that we have stored a value, we can use this variable in any expression. If Lisp encounters it, it will replace the variable by the value we stored there.
CL-USER> (* 52 days-in-a-week) 364
The Python equivalent would be
days_in_a_week = 7
52 * days_in_a_week
364
Note how the convention in Python is to use underscores as separators. In fact,
Python would parse days-in-a-week as the subtraction days - in - a - week.
The convention in Lisp is to use dashes as separators. Indeed, because of the
simplicity of Lisp's syntax, you can use many other characters in variable
names and in symbols. For example, in Lisp this is just a symbol:
CL-USER> 'a*x^2+b*x+c A*X^2+B*X+C
List operators
If you have a list, you can construct a new list consisting of a value followed by the elements of the list as follows.
CL-USER> (cons 'a '(b c d)) (A B C D)
We can retrieve the first element of a list and the rest of the list as follows:
CL-USER> (first '(a b c d)) A CL-USER> (rest '(a b c d)) (B C D)
For historical reasons first and rest are also known as car and cdr.
CL-USER> (car '(a b c d)) A CL-USER> (cdr '(a b c d)) (B C D)
A range of functions of the form cadadr are also available for digging more
deeply into list structure.
CL-USER> (cadadr '(a (b c) d e)) c
This translates to
CL-USER> (car (cdr (car (cdr '(a (b c) d e))))) c
We can ask whether something is a list with listp. The p at the end stands
for predicate, i.e. something that returns a Boolean. It is a convention in
Lisp to end a predicate with p or -p. Compare this to Racket's convention
of ending a predicate with a question mark.
CL-USER> (listp '(a b c)) T CL-USER> (listp (list 1 2 3)) T CL-USER> (listp "nope") NIL
We can ask whether a list is empty using null
CL-USER> (null ()) T CL-USER> (null nil) T CL-USER> (null '(1 2 3)) NIL
Indeed, null is an exception to the above convention.
Defining functions
We have seen how to store values in variables. How do we define a function? Let's jump straight in with a numerical function.
(defun fact (n) "The factorial function" (if (zerop n) 1 (* n (fact (- n 1)))))
This is a definition of the factorial function. Racket programmers will note
that it's almost identical to what one might write in Racket. The only
differences are defun for define and the function name outside the
parentheses. Let's look at it piece by piece.
- After
defunis the name of the function being defined:fact - It is followed by a list of parameters. This function will take a single
argument named
n. - The second line contains a(n optional) documentation string. This is just like in Python except that a multi-line string in Lisp is still delimited by single double quotes.
- The
ifexpression takes three arguments; the first is the test, the second the true case and the third the false case. Ifis an expression, not a statement. It returns either the value of its second argument or the value of its third argument, depending on the value of its first argument. In Pythonifis a statement; it does not return anything.- No explicit
returnis required.
Expressions
- In Python you have expressions and statements.
This is an expression:
x = 3 + 4 * math.sqrt(69)And expression gets evaluated to produce a value.
This is a statement:
x = -3 if x > 0: print("x is positive") else: print("x is not positive")
x is not positive
A statement is not evaluated. it does not produce a value. It is executed according to the semantics of, in this case, the
ifstatement.Note that Python allows for functions that return any number of values. To return more than one value, return a tuple. An example of a function which returns two values is
math.modfwhich returns the fractional and integer parts of its argument.import math frac, whole = math.modf(23.45) print(f"The whole part is {whole}\nThe fractional part is {frac}")
The whole part is 23.0 The fractional part is 0.4499999999999993
You can write a function which returns multiple values like this.
def three_harmonics(n): return 1 / n, 2 / n, 3 / n three_harmonics(5)
(0.2, 0.4, 0.6)
- To return no value, depending on your needs you might return the empty tuple
()or simply not return anything in which case the function returnsNone In Lisp, everything is an expression, even constructs that you might think of as statements, such as
ifandsetf. This means that we can write(setf v (if (> 3 4) (+ 5 6) (+ 7 8)))
Here the
ifreturns the value of its else case, namely 15.Setfstores that value in the variablevand then returns it.In Python we might write
def announce_bananas(n): if n > 0: print("We have some bananas today.") else: print("We have no bananas today.")
If it were something more complex you might write
def announce_bananas(n): if n > 0: amount = "some" else: amount = "no" print(f"We have {amount} bananas today.")
In Lisp you would simply write
(defun announce-bananas (n) (format t "We have ~a bananas today." (if (> n 0) "some" "no"))) (announce-bananas 23) (announce-bananas 0)
In Lisp a function can return more, or fewer, than one value. An example of a function that returns two values is
floor* (floor 7 4) 1 3 *
To capture multiple values, use
multiple-value-bind(multiple-value-bind (times remainder) (floor 7 4) (format nil "Four goes into seven ~r time~p with ~r left over" times times remainder))
In passing, note the pluralisation directive
~pwhich pluralises the previous word if the corresponding argument requires that. It's not very clever though.(format nil "mouse~p" 2) => "mouses"
Also note the
~rdirective which prints a number as a word or even Roman numerals.(format nil "The year ~@r is ~r." 2022 2022) "The year MMXXII is two thousand twenty-two."
See also A Few FORMAT Recipes
In Lisp you can return more, or fewer, than one value using the
valuesform. Here is an example of a function that returns two values.(defun polar (x y) (values (sqrt (+ (* x x) (* y y))) (atan y x)))
You can return zero values with simply
(values)
Housekeeping
- Chrestomathy
- Rosetta Code
- What should (not) go in your git repo?
- Assignment 2: recursion exercises
- Exam 1
- Come 15 minutes early
- May not leave early
- Mock exam available this Thursday