common lisp
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
(loop for x in '(a b c)
for y in '(1 2 3)
collect (list x y))
;; ((A 1) (B 2) (C 3))
(loop for x in '(a b c d e)
for y from 1
if (> y 1)
do (format t ", ~A" x)
else
do (format t "~A" x))
;; A, B, C, D, E
;; NILfunctions
(defun <name> (list of arguments)
"docstring"
(body))plist
defining a plist(setf my-plist '(alt "yes" bar "no"))(getf my-plist 'alt)(setf (getf my-plist 'alt) 45)(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 afaikcurl -O https://beta.quicklisp.org/quicklisp.lisp
sbcl --load quicklisp.lisp(quicklisp-quickstart:install :path "~/.quicklisp")
(ql:add-to-init-file)(ql:quickload "cl-csv")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)(defclass my-class (my-parent-class))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)) ==> 3get nth element:
(defparameter *x* (vector 1 2 3))
(length *x*) ==> 3
(elt *x* 1) ==> 2we 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 sbclsb-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))))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(defun setq2-function (v1 v2 e)
(list 'progn (list 'setq v1 e) (list 'setq v2 e)))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")) => nothingfrom 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)
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))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))
2reader 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 ()
...)*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)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 implementationstring equality:
(equal "foo" (copy-seq "foo")) ;; => T(eq 'foo 'foo) ;; => T;; '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(eq 'a 'a) ;; => T
(eq "foo" (copy-seq "foo")) ;; => NIL
(eq 3 3.0) ;; => NILuppercase 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))) ;; => Talist
we can append new pairs to an alist usingpush (there are many other ways):
(let ((my-alist '((a . b))))
(push (cons 'x 'y) my-alist)
(push '(10 . 20) my-alist))
multiple value returns
(defun polar (x y)
(values (sqrt (+ (* x x) (* y y))) (atan y x)))
(multiple-value-bind (r theta) (polar 3.0 4.0)
(vector r theta))files
https://lispcookbook.github.io/cl-cookbook/files.htmlbasename
(pathname-name "test1/test.org") ;; testfile extension
(pathname-type "test1/test.org") ;; orgparent directory
(pathname-directory "test1/test.org") ;; (:RELATIVE "test1")
(car (last (pathname-directory "test1/test.org"))) ;; "test1"
(uiop:pathname-parent-directory-pathname "./test1/test.org") ;; #P"./"check if file exists
(probe-file "/etc/passwd") ;; #P"/etc/passwd"
(probe-file "foo") ;; NIL
(uiop:probe-file* (uiop:parse-unix-namestring "/home/mahmooz/data/got/[Algorithms and Combinatorics №27] Stasys Jukna (auth.) - Boolean Function Complexity_ Advances and Frontiers (2012, Springer) [10.1007_978-3-642-24508-4] - libgen.li-0.png")) ;; handles [ ] properly while probe-file errors outexpanding tilde
(uiop:native-namestring "~/.emacs.d/") ;; "/home/mahmooz/.emacs.d/"read file
(uiop:read-file-string "file.txt")
(uiop:read-file-lines "file.txt")
(uiop:read-file-string (uiop:parse-unix-namestring "path")) ;; handles [ ] (and probably other pathological patterns in filenames) properlywrite to file
(alexandria:write-string-into-file content "file.txt")
(str:to-file "file.txt" content)
(with-open-file (f "/home/pedro/miscellaneous/misc/tests-output/stack-overflow.lisp"
:direction :output
:if-exists :overwrite
:if-does-not-exist :create)
(write-sequence "hello" f)
(pprint (list example-1 example-2) str))join paths
(merge-pathnames "otherpath" "/home/mahmooz/") ;; #P"/home/mahmooz/otherpath"
(namestring (merge-pathnames "otherpath" "/home/mahmooz/")) ;; "/home/mahmooz/otherpath"path to string
(namestring #P"test/test.org") ;; "test/test.org"get current directory
(uiop/os:getcwd) ;; #P"/home/mahmooz/work/emacs.d/"change working directory
(uiop:chdir "/bin/")
(let ((dir "/path/to/another/directory/")) ;; only set cwd lexically
(uiop:with-current-directory (dir)
(directory-files "./")))list directory
(uiop:directory-files "./")
(uiop:subdirectories "./")
(directory #P"*.jpg")walk directory
walking a directory recursively is possible withcl-fad:
(let (files)
(cl-fad:walk-directory "/tmp/" (lambda (x) (push (namestring x) files)))
(car files))
MOP
MOP refers to "The Common Lisp Object System MetaObject Protocol".in the following example, we use an sbcl-specific mop package named
sb-mop, other common lisp implementations may have similar functionality in a differently named package.
;; base class with "static" property `rule' that is shared by all instances
(defclass other-text-object (text-object)
((rule
:accessor other-text-object-rule
:allocation :class
:documentation "the matching method from `*matching-methods*' used to match against the text object.")))
;; define the `inline-math' subclass with its own default value for `rule'
(defclass inline-math (other-text-object)
((rule
:allocation :class
:initform (list '(:name inline-math
:method pair
:data ("\\(" :string "\\)" :string)
:nestable nil)))))
;; we need to "finalize" the class to be able to use MOP
(sb-mop:finalize-inheritance (find-class 'inline-math))
;; then we can grab the rule from the class itself:
(slot-value (sb-mop:class-prototype (find-class 'inline-math)) 'rule),#+sbcl (:import-from "SB-MOP" "SLOT-VALUE-USING-CLASS")
,#+cmu (:import-from "MOP" "SLOT-VALUE-USING-CLASS")common sequence operations
mapping sequence
(mapcar '1+ (loop for i from 0 to 10 collect i))
filtering sequence
(format t "~A" (remove-if-not 'identity (list nil 1 2 3 nil)))
(terpri)
(format t "~A" (remove-if 'identity (list nil 1 2 3 nil)))
finding in a sequence
(find-if 'symbolp (list 1 2 3 'test))
reducing a sequence
(reduce '+ (list 5 6 10))
concatenating sequences
(format t "~A" (concatenate 'string "test1" "test2"))
(terpri)
(format t "~A" (concatenate 'list (list 1 2 3) (list 4 5)))
zipping two sequences
(pairlis '(a b c) '(1 2 3))
regex
usingcl-ppcre. docs at https://edicl.github.io/cl-ppcre/.
regex simple matching
this returns the indicies bounding the matching string.(format t "1. ~A~%" (cl-ppcre:scan "[a-z]{8}" "this worddddd is matched"))
(format t "2. ~A~%" (cl-ppcre:all-matches "[a-z]{8}" "this worddddd is matched"))
(format t "3. ~A~%" (cl-ppcre:all-matches "[a-z]{5}" "more than one match"))
(format t "4. ~A~%" (cl-ppcre:all-matches "[a-z]{5}" "more than one match match match"))
regex group matching
(cl-ppcre:register-groups-bind (kw val)
("\\+([a-z]+): (.*)" "#+mykeyword: myvalue")
(cons kw val))
embedded modifiers
different embedded modifiers are supported, like(?i) to enable case-insensitive matching.
(cl-ppcre:all-matches "(?i)word" "this is an uppercase WORD that is matched")
communicating with subprocesses
https://stackoverflow.com/questions/15988870/how-to-interact-with-a-process-input-output-in-sbcl-common-lisp(defun program-stream (program &optional args)
(let ((process (sb-ext:run-program program args
:input :stream
:output :stream
:wait nil
:search t)))
(when process
(make-two-way-stream (sb-ext:process-output process)
(sb-ext:process-input process)))))
(defvar *stream* (program-stream "cat"))
(format *stream* "foo bar baz~%")
(finish-output *stream*)
(read-line *stream*)
(close *stream*)
;; CL-USER> (defparameter *stream* (program-stream "python" '("-i")))
;; *STREAM*
;; CL-USER> (loop while (read-char-no-hang *stream*)) ; skip startup message
;; NIL
;; CL-USER> (format *stream* "1+2~%")
;; NIL
;; CL-USER> (finish-output *stream*)
;; NIL
;; CL-USER> (read-line *stream*)
;; "3"
;; NIL
;; CL-USER> (close *stream*)
;; T
;; CL-USER> (defparameter *stream* (program-stream "unbuffer"
;; '("-p" "tr" "a-z" "A-Z")))
;; *STREAM*
;; CL-USER> (format *stream* "foo bar baz~%")
;; NIL
;; CL-USER> (finish-output *stream*)
;; NIL
;; CL-USER> (read-line *stream*)
;; "FOO BAR BAZ
;; "
;; NIL
;; CL-USER> (close *stream*)
Tget definition/source of function
(defun test ()
(+ (- 1 2) 3))
(function-lambda-expression #'test)
check if symbol refers to a function (if function is bound)
CLTPT> (fboundp 'identity)
#<FUNCTION IDENTITY>
CLTPT> (fboundp 'identity2)
NILwhen-let
(defmacro when-let (bindings &body body)
"bind multiple VARS to their FORMS like `let`. if all are non-nil,
execute BODY with those bindings in effect, otherwise return NIL."
(let ((bnds (if (and (consp bindings)
(consp (first bindings))
(symbolp (caar bindings)))
bindings
(list bindings))))
`(let ,bnds
(when (and ,@(mapcar #'car bnds))
,@body))))
(defmacro when-let* (bindings &body body)
"like LET*, but if every FORM in BINDINGS evaluates non-nil,
execute BODY, otherwise return NIL immediately."
(if (null bindings)
`(progn ,@body)
(destructuring-bind ((var form) . rest) bindings
`(let ((,var ,form))
(when ,var
(when-let* ,rest ,@body))))))some code for trees i guess
(defun make-node (interval)
(cons interval nil))
(defun intervals-conflict-p (c-start c-end p-start p-end)
"checks if two intervals (c-start, c-end) and (p-start, p-end)
overlap AND neither contains the other."
(let ((overlap (< (max c-start p-start) (min c-end p-end))))
(when overlap
(let ((c-contains-p (and (<= c-start p-start) (>= c-end p-end)))
(p-contains-c (and (<= p-start c-start) (>= p-end c-end))))
(not (or c-contains-p p-contains-c))))))
(defun build-forest (intervals)
"build a nested tree from a list of intervals, discarding entries
that overlap with a sibling or a root without being contained by it (or containing it).
each interval is a list of the form (start end id)."
(let ((forest)
(stack))
(setf intervals (sort (copy-list intervals) #'< :key #'first))
(dolist (current-interval-data intervals)
(let ((node-to-add (make-node current-interval-data))
(c-start (first current-interval-data))
(c-end (second current-interval-data))
(discard-current))
(loop while (and stack
(let* ((parent-node-on-stack (first stack))
(p-interval-on-stack (car parent-node-on-stack))
(p-start (first p-interval-on-stack))
(p-end (second p-interval-on-stack)))
(not (and (<= p-start c-start) (>= p-end c-end)))))
do (pop stack))
(let ((potential-parent-node (first stack)))
(if potential-parent-node
(dolist (sibling-node (cdr potential-parent-node))
(when (not discard-current)
(let* ((s-interval-data (car sibling-node))
(s-start (first s-interval-data))
(s-end (second s-interval-data)))
(when (intervals-conflict-p c-start c-end s-start s-end)
(setf discard-current t)))))
(dolist (existing-root-node forest)
(when (not discard-current)
(let* ((r-interval-data (car existing-root-node))
(r-start (first r-interval-data))
(r-end (second r-interval-data)))
(when (intervals-conflict-p c-start c-end r-start r-end)
(setf discard-current t)))))))
(unless discard-current
(if (first stack)
(progn
(push node-to-add (cdr (first stack)))
(push node-to-add stack))
(progn
(push node-to-add forest)
(push node-to-add stack))))))
(nreverse forest)))
(defun print-node (node &optional (indent 0))
"recursively prints a node and its children with indentation."
(format t "~v@T~a~%" indent (car node)) ;; print the node's interval with indentation.
(dolist (child (cdr node))
(print-node child (+ indent 2)))) ;; increase indent for children.
(defun print-forest (forest)
"prints all trees in the forest."
(dolist (node forest)
(print-node node 0)))
(defun mapcar-tree (node func)
(cons (funcall func (car node))
(mapcar (lambda (child)
(mapcar-tree child func))
(cdr node))))
(defun mapcar-forest (forest func)
"apply `mapcar-tree' to every node in the forest."
(mapcar (lambda (node)
(mapcar-tree node func))
forest))
(defun map-tree (node func)
(let ((result (mapcar (lambda (child)
(map-tree child func))
(cdr node))))
(cons (funcall func node)
result)))
(defun map-forest (forest func)
"apply map-tree to every node in the forest."
(mapcar (lambda (node)
(map-tree node func))
forest))
(defun tree-mapcar (node func &optional cond)
"we iterate through the tree one NODE at a time and run FUNC on each, COND
decides whether to recurse on a specific node. a tree is returned with nodes
replaced by the results of calling FUNC on them. children are handled first."
(if cond
(if (and (consp node) (funcall cond node))
(cons (funcall func (car node))
(loop for child in (cdr node)
collect (tree-mapcar child func cond)))
(cons (funcall func (car node))
(cdr node)))
(if (consp node)
(cons (funcall func (car node))
(loop for child in (cdr node)
collect (tree-mapcar child func cond)))
node)))
(defun tree-find (node item &key (test #'equal) (key #'identity))
(tree-mapcar
node
(lambda (other-node)
(when (funcall test item (funcall key other-node))
(return-from tree-find other-node))))
nil)