for loop
(loop for x in '(1 2 3) collect x) ;; returns list (loop for x in '(1 2 3) do (print x)) ;; prints the numbers
functions
(defun <name> (list of arguments) "docstring" (body))
plist
defining a plist
(setf my-plist '(alt "yes" bar "no"))
retrieve a value by key
(getf my-plist 'alt)
change the value that corresponds to a key
(setf (getf my-plist 'alt) 45)
get a list of the attributes
(loop for (key value) on my-plist by #'cddr collect key)
unbind variable
(makunbound 'foo)
undefine funciton
(fmakunbound 'function)
quicklisp
need to install it manually afaik
curl -O https://beta.quicklisp.org/quicklisp.lisp sbcl --load quicklisp.lisp
then in sbcl
(quicklisp-quickstart:install :path "~/.quicklisp") (ql:add-to-init-file)
then to install a package in sbcl:
(ql:quickload "cl-csv")
this will install the package if it doesnt exist, and if it exists it just loads it
classes
example class:
(defclass my-class () ((property1 :accessor property1-accessor) (property2 :accessor property2-accessor)))
the accessor
is the name you use to access a property1
we construct an object using make-instance
:
(defvar p1 (make-instance 'my-class))
we can initialize the class with specific values, first we need to use the :initarg
keyword on the class properties
(defclass my-class () ((property1 :initarg property1-initarg :initform default-value :accessor property1-accessor) (property2 :accessor property2-accessor)))
then we can pass the values we want to make-instance
:initform
is used to pass a default value to the slot incase :initarg
argument isnt passed to make-instance
(defvar p1 (make-instance 'my-class :property1-initarg property1-value))
this is the syntax for accessing variables of a class
(property1-accessor p1)
we can do inheritance by passing the parent class in the parenthases after the class name:
(defclass my-class (my-parent-class))
the following syntax is used to define a function for a class, note that the function print-object
is the function that gets called when trying to print an object using (print object)
so thats what we're defining here
(defmethod print-object ((obj my-class) stream) (print-unreadable-object (obj stream :type t) (format stream "value of property1: ~a" (property1 obj))))
structs
structs are basically simpler classes with default functions/initializers/etc
(defstruct rgb () (r 0) (g 0) (b 0)) ;; initialize slot values to 0 ;; e.g. constructor built by default (setf color (make-rgb :r 2)) ;; => #S(RGB :NIL NIL :R 2 :G 0 :B 0) (list (rgb-r color) (rgb-b color)) ;; => (2 0)
arrays/matrices
to create an array:
(make-array 10 :initial-element 10)
10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 |
multidimensional arrays:
(make-array '(4 3) :initial-element 15)
#2A((15 15 15) (15 15 15) (15 15 15) (15 15 15))
access slot in array
(aref (make-array '(4 3 1) :initial-element 15) 2 0 0)
15
get the length of the array (last argument is the dimension to check length of):
(print (array-dimension (make-array '(4 3) :initial-element 15) 0)) (print (array-dimension (make-array '(4 3) :initial-element 15) 1))
4 3
iterate through array:
(map nil #'print (make-array 3 :initial-element 15))
15 15 15
iterate with index:
(let ((arr (make-array '(4 3) :initial-element 15))) (loop for i from 0 below (array-dimension arr 0) do (print (aref arr i 1))))
15 15 15 15
use displacement arrays to get the first element of every row in a 2d array:
(loop for i from 0 below (array-dimension arr 0) collect (let ((row (make-array (array-dimension arr 1) :displaced-to arr :displaced-index-offset (* i (array-dimension arr 1))))) (aref row 0)))
we can create resizable arrays with the :adjustable
keyword, :fill-pointer
denotes the next position to be filled in the array and is automatically maintained by the array itself, but has to be initialized
(defparameter *x* (make-array 0 :adjustable t :fill-pointer 0)) (vector-push-extend 'a *x*) (vector-push-extend 'b *x*) *x*
A | B |
fill array manually:
(make-array '(3 3) :initial-contents '((1 0 0) (0 1 0) (0 0 1)))
vectors
create vector
(vector 3 5 1)
we can use length
on vectors
(length (vector 3 5 1)) ==> 3
get nth element:
(defparameter *x* (vector 1 2 3)) (length *x*) ==> 3 (elt *x* 1) ==> 2
we can have nested vectors
(let ((a (vector (vector 1 2) (vector 3) 4))) (print a) (print (elt a 0)))
#(#(1 2) #(3) 4) #(1 2)
multithreading
in sbcl sb-thread:make-thread
takes a function to call in a newly created thread.
(sb-thread:make-thread (lambda () (progn (sleep 0) ;; give other threads a chance to run, then return here (setf c (+ a b)) (print "ADDITION:") (print c))))
although notice that this is special to sbcl.
the lparallel library works great on multiple CL implementations, more on lparallel at https://lparallel.org/page/2/
macros
simple macro from https://lispcookbook.github.io/cl-cookbook/macros.html:
(defmacro setq2 (v1 v2 e) (list 'progn (list 'setq v1 e) (list 'setq v2 e))) ;; (setq2 v1 v2 3) => v1=3,v2=3
this macro is very close to the following function definition:
(defun setq2-function (v1 v2 e) (list 'progn (list 'setq v1 e) (list 'setq v2 e)))
consider the following macro when2
which behaves like the builtin when
(defmacro when2 (condition &rest body) `(if ,condition (progn ,@body))) ;; (when2 t (print "hey")) => "hey" ;; (when2 nil (print "hey")) => nothing
from http://www.lispworks.com/documentation/HyperSpec/Body/02_df.htm
if a comma is immediately followed by an at-sign, then the form following the at-sign is evaluated to produce a list of objects. these objects are then “spliced” into place in the template. for example, if x has the value (a b c), then
`(x ,x ,@x foo ,(cadr x) bar ,(cdr x) baz ,@(cdr x))
=> (x (a b c) a b c foo b bar (b c) baz b c)
body
is just a list and can be used/modified
(defmacro test-macro (&rest body) `(quote ,body))
this is called an anaphoric macro where you wrap the body with a lexical function definition that shadows the global one
CL-USER> (defun foo () 1) FOO CL-USER> (defmacro with-different-foo (&body body) `(flet ((foo () 2)) ,@body)) WITH-DIFFERENT-FOO CL-USER> (progn (foo)) 1 CL-USER> (with-different-foo (foo)) 2
reader macros
the #+
and #-
reader macros are pretty nice for commenting out sexps. they allow ignoring the following sexp, if the given symbol isn't/is found in *FEATURES*
. Just pick a symbol not in *FEATURES*
, and use it with #+
like this:
#+nil (defun foo () ...)
Now, the function definition will be ignored (unless NIL is in *FEATURES*
, which is not very likely).
use double-floats by default
(setf *read-default-float-format* 'double-float)
enforce garbage collection
running
(gc :full t)
invokes the garbage collector
checking equalities
some equality tests may return results that are implementation-specific, i dont list those here, i only wrote the ones that are supposed to work on any cl implementation
string equality:
(equal "foo" (copy-seq "foo")) ;; => T
symbol equality (any equality operator works for symbols):
(eq 'foo 'foo) ;; => T
number equality:
;; 'eql' is "type sensitive" (eql 3 3.0) ;; => NIL (eql 3 3) ;; => T ;; 'equal' works for floats and integers all the same (equal 3 3.0) ;; => T (equal 3 3) ;; => T ;; 'equalp' works for floats and integers all the same (equalp 3 3.0) ;; => T (equalp 3 3) ;; => T
object equality (same object in memory):
(eq 'a 'a) ;; => T (eq "foo" (copy-seq "foo")) ;; => NIL (eq 3 3.0) ;; => NIL
sequence equality (and general object equality which compares the internal structure):
uppercase and lowercase letters in strings are considered by equal
to be distinct. in contrast, equalp
ignores case distinctions in strings. equalp
works for arrays but equal
doesnt ("works" as in does element-wise comparison, which is the intended equality check here)
;; strings (equal "Foo" (copy-seq "Foo")) ;; => T (equal "Foo" (copy-seq "foo")) ;; => NIL (equalp "Foo" (copy-seq "foo")) ;; => NIL ;; vectors/arrays (equal #(1 2 3) (copy-seq #(1 2 3))) ;; => NIL (equalp #(1 2 3) (copy-seq #(1 2 3))) ;; => T ;; lists (equal '(1 2 3) (copy-seq '(1 2 3))) ;; => T (equalp '(1 2 3) (copy-seq '(1 2 3))) ;; => T
alist
we can append new pairs to an alist using push
(there are many other ways):
(let ((my-alist '((a . b)))) (push (cons 'x 'y) my-alist) (push '(10 . 20) my-alist))
((10 . 20) (X . Y) (A . B))