📝Common Lisp
Common Lisp is a practical lisp
Lisp exposes two levels of syntax (s-expressions and lisp forms)
Syntax
CL has exact ratios:
CL-USER> (+ 3/7 5/4) 47/28
- Symbols:
- When reading symbol, the reader converts all unescaped chars to uppercase
- Common conventions:
*global-variable*
%low-level-function
or%%low-level-function
(used by some programmers)something->other
— conversion function
- reader macros
'x
→(quote x)
#'x
→(function x)
Evaluation
It is possible for symbols to be self-evaluated—the value of the variable is the symbol itself
(defconstant t 't) (defconstant nil 'nil)
When the reader interns keyword, it automatically defines a constant variable with the name and with the symbol as the value
(defconstant :hello ':hello)
(keywords are just symbols starting with
:
)- lists:
- function call forms
- macro forms
- special forms
- 25 of them (if, let, quote)
nil
is false, everything else is truenil
is both an atom and a list.(eq nil ()) => T
Functions
;; general form
(defun name (parameters)
"docstring."
body)
;; optional parameters
(defun f (a b &optional c d) …)
;; specifying default value
(defun f (&optional (a 10)) …)
;; to know whether value was supplied
(defun f (&optional (a 3 a-supplied-p)) …)
;; “-supplied-p” suffix is a convention for such variables
;; rest
(defun f (a b &rest cs) …)
;; keyword
(defun f (&key a b c) …)
;; … then called
(f :a a-value :c c-value)
;; can also supply default value and a supplied-p
(defun f (&key (a 10 a-supplied-p)) …)
;; keyword can also be customized to be different from variable name
(defun f (&key ((:apple a)) ((:box b) 0 b-supplied-p)) …)
;; order:
;; 1. required
;; 2. &optional
;; 3. &rest
;; 4. &key
;; When both optional and keyword parameters are used, optional will
;; eat keywords until optional parameters are satisfied.
;;
;; You should probably avoid using optional and key arguments
;; together.
(defun foo (x &optional y &key z) (list x y z))
(foo 1 :z 3) ; => ERROR
(defun bar (a &optional b c &key d) (list a b c d))
(bar 1 :d 2) ; => (1 :D 2 NIL)
;; When both rest and keyword parameters are used, the arguments are
;; filled in both rest and keyword parameters.
(defun baz (&rest rest &key a b c) (list rest a b c))
(baz :a 1 :b 2 :c 3) ; => ((:A 1 :B 2 :C 3) 1 2 3)
;; This definition only allows specified keyword arguments and no
;; other arguments:
(baz "hello" "world") ; => ERROR: Unknown &KEY argument: "hello"
(baz :hello "world") ; => ERROR: Unknown &KEY argument: :HELLO
;; To allow other arguments, add &allow-other-keys to the parameters
;; list
;; RETURN-FROM operator can be used to return value from the function
;; immediately
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
(return-from foo (list i j))))))
;; get function object
#'foo
;; call it
(funcall #'foo 20)
(apply #'foo '(20))
;; anonymous functions
(lambda (parameters) body)
Variables
;;; defining global variables
;; If variable is already defined, its value is not overwritten.
(defvar *name* optional-value
"optional docstring.")
;; `defparameter' always assigns the new value.
(defparameter *name* value
"optional docstring.")
;; `defconstant' name cannot be used as a function parameter or
;; rebound with any other binding form. Many Lisp programmers follow
;; the plus +naming+ +convention+ for constants.
(defconstant name initial-value-form
"optional docstring.")
;; All global variables are dynamically-scoped. (The name of every
;; variable defined with `defvar' or `defparameter' is automatically
;; declared “special.”) LET variables or function parameters can
;; override it.
(defvar *x* 10)
(defun foo () (format t "X: ~d~%" *x*))
(foo) ; => X: 10
(let ((*x* 20)) (foo)) ; => X: 20
(foo) ; => X: 10
In Common Lisp, all global variables are automatically “special” (use dynamic scoping)
In Common Lisp, global variables use star *naming*
*convention*
because of dynamic scoping
Loops
;; basic form
(do (variable-definition*)
(end-test-form result-form*)
statement*)
;; where variable-definition =
(var init-form step-form)
;; LOOP simple form—an infinite loop. Iterates forever unless you
;; `return' or break out.
(loop
body-form*)
Macros
;; Macro parameters are destructuring. Macros can also have &body
;; parameters that work identically to &rest but IDEs can use it to
;; properly indent macros.
(defmacro do-primes ((var start end) &body body)
…)
;; Use `gensym' if you need to generate a unique symbol.
(defmacro with-gensyms ((&rest names) &body body)
`(let ,(loop for n in names collect `(,n (gensym)))
,@body))
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,@(loop for g in gensyms collect `(,g (gensym))))
`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
,@body)))))
Data structures
Association list
- It’s a list of cons cells.
assoc
find cons cell in a list;; search by key (assoc 'a '((a . 1) (b . 2))) ; => (A . 1) ;; search by value (rassoc 2 '((a . 1) (b . 2))) ; => (B . 2)
add new value to the front
(cons (cons 'new-key 'new-value) alist) ;; same as (acons 'new-key 'new-value alist)
Property list
- It’s a list of alternating key-values
Get element
;; `getf'/`remf' always use `eq' for comparison. ;; get value (getf '(:hello "world" :hi 5) :hi) ; => 5 ;; set value (setf (getf *plist* :a) 2) ;; remove value (remf *plist* :a) ;; `get-properties' to extract one of multiple properties. (defun process-properties (plist keys) (loop while plist do (multiple-value-bind (key value tail) (get-properties plist keys) (when key (process-property key value)) (setf plist (cddr tail))))) ;; Every symbol object has an associated property list that you can ;; get with `symbol-plist' function. (symbol-plist '*x*) ;; There is a shortcut to get the necessary property rather than the ;; whole list. (get 'symbol 'key) ; === (getf (symbol-plist 'symbol) 'key) ;; `get' is `setf'able (setf (get 'symbol 'key) "information") ;; to remove property: (remprop 'symbol 'key) ; === (remf (symbol-plist 'symbol) 'key)
Common Lisp: Multiple Dispatch
Common Lisp: classes
Slots inheritance
During inheritance, all slot specifiers with the same name are merged to create a single slot specifier.
- for
:initform
, the most specific class wins :initarg
values are merged to allow any of individual:initarg
to initialize the slot- If the caller passes multiple keyword argument for the same field, the leftmost keyword in invocation wins
:reader
,:writer
,:accessor
are not merged because previously created accessor functions will apply to the new class already:allocation
is determined by the most specific class- if subclass specified a
:class
slot, the slot will by shared by that subclass only (not shared with the original superclass)
- if subclass specified a
Multiple inheritance and class specificity
- classes specified earlier in
defclass
direct superclass list are considered more specific