đź“ť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 slotIf 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 classif subclass specified a
:class
slot, the slot will by shared by that subclass only (not shared with the original superclass)
Multiple inheritance and class specificity
classes specified earlier in
defclass
direct superclass list are considered more specific