Skip to content

Instantly share code, notes, and snippets.

@jmercouris
Created February 8, 2018 18:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmercouris/79c168a6d618a1ac14d78957e4b47e4c to your computer and use it in GitHub Desktop.
Save jmercouris/79c168a6d618a1ac14d78957e4b47e4c to your computer and use it in GitHub Desktop.

REPL

  • Printing is done by the (format) defun, the signature can be seen in the minibuffer
  • (defun name (args) (body))
  • Load external files via load command (load “path/file.lisp”)
  • slime-load-file (C-c C-l)
(princ message)
  • Plists, propertylists, have args for each element (list :a 1 :b 2 :c 3)
  • Mixed list plist, regular list elements come after demarcated elements (list :a 1 2 3 4)
  • (getf) plist + symbol, return value in plist with symbol (getf (list :a 1 :b 2) :a) Return: 1
  • PUSH adds items to a list
  • dolist can iterate through a list, first arg = placeholder, second arg = list item
  • (format) all args start with ~ {} will loop over a list doing for each element a will print aesthetically pleasing 10t will tab to 10th column % will create a newline
  • (read-line) is used to get in put from the user
  • (force-output) is necessary on some lisp because they will not print without a newline invokation
  • \*query-io* global var that contains access to term input stream
  • (parse-integer) grab integer from string
  • y-or-n-p get yes or no response from user
  • loop will loop until a (return) statement encountered
  • macros can have non-standard formatting, e.g. with-open-file
  • with-open-file
    • direction = :input or :output
  • #’ == get me function with the following name
  • “lambda” indicator of anonymous function definition

Syntax and Semantics

  • two phases
    • reader, translate into lisp form
    • evaluator, evaluate lisp obj generated by reader

Reader

  • s-expressions are composed of lists and atoms
  • lists are delimted by parens
  • atoms are everything else
  • the elements of lists are themselves s-expressions (atoms, or nested lists)
  • numbers = 3/6, 123 1.0e0, 1.0d0, -42
  • strings are enclosed by quotes
  • backslash escapes next char
  • only char that must be escaped is double quotes and backslash itself
  • names used in lisp programs e.g. db, FORMAT, represented by objects called symbols
  • all symbols are converted to uppercase
  • name scope extends to all child s-expressions as a reference rather than value
  • use hypenated-names
  • globals vars
  • contant vars+
  • low-level %functions or low low level %%functions

Evaluator

  • not all s-expressions are evaluatable, consider a list of numbers
  • any atom (nonlist, or empty list) or list that has a symbols as its first element is a legal lisp form
  • atom lisp form = two cat, symbols, everything else
  • symbol is evaluated as a form, is consdered the name of a variable, and evaluates to the current value of the variable
  • any non-symbol atom is self-evaluating object e.g. “text” returns “text” in the repl
  • symbols can also be self-evaluating in sense that “T” translates to true
  • other self eval symobls are :keyword-symbols, reader defines a constant with the name, and the symbol as the value
  • three types of list eval, function call forms, macro forms, special forms

Function Calls

  • eval for func calls is simple, eval remaining elements of list as lisp forms, and pass the resultant values to the named functions
  • all elements of list after the first must be well formed- lisp forms
  • (function-name argument*), where each argument is itself a lisp form
  • quote special form that takes single expression as argument and simply returns it e.g. (quote (+ 1 2)) evalutes to the list (+ 1 2), not 3
  • quote is so prevalent that you can just write ‘(+ 1 2), is quicker
  • let is used to create new var bindings in a scope
  • (let ((x 10)) x)

Macros

  • macros are a way to extend the language syntax
  • macro is a func that takes s-expression as args, and returns a lisp form that is then evaluated in place of the macro form
  • eval in two phases
    • elements of macro form are passed to macro function
    • form returned by macro (expansion) evaluated according to normal rules
  • when lisp is compiled, expansion and eval happening at very diff times
  • basically they are like c macros, can do some crazy substitution, a lot of it, before the program actually runs to save cpu time
  • because eval doesnt eval elements of macro form, they do not need to be well-formed lisp forms
  • the macro will always be responsible for making the well-formed syntax
  • e.g. an expression that is backwards passed to the backwards macro will execute legally, when it would not otherwise

Truth, Falsehood, and Equality

  • nil is only false value
  • nil is both and atom and a list, it represents the empty list
  • () === nil in reader
  • ‘t and ‘nil will eval to the just T or NIL
  • = is used to cmpare numbers
  • CHAR= to compare chars
  • EQ, EQL, EQUAL, EQUALP operators in order of discrimination
    • EQ obj identity test, depends on implementation
    • EQL consider two obj of same class representing same value as equal e.g. 1, 1 and 1, 1.0 is false, since int and float point diff classes
    • EQUAL, less discriminatory for lists and strings
    • EQUALP even less discriminatory, strings same ignoring case 1.0, 1 equal etc

Formatting Lisp Code

  • items at the same level of nesting are lined up
  • macro and special forms are indented diff, body is indented two spaces relative to opening parens of form
  • closing parens are always put on the same line as the last element of the list they are closing
  • ;;;; file header comment
  • ;;; comment with three semicolons paragraph comment for large section of code
  • ;; two semicolons for code that follows, same indent level of code it comments on
  • ; applies to one line

Functions

  • functions are defined like this

(defun name (parameter*) “Documentation String” body-form*)

  • conversion functions use -> in the name
    • e.g. string->widget
  • no args to function is ()
  • value of last expression is returned as value of the function
  • RETURN-FROM operator can return immediately anywhere in the function

(defun verbose-sum (x y) “Sum any two numbers after printing a message” (format t “summing -d and -d -%” x y) (+ x y))

to define optional parameters use &optional

  • eg. (&optional arg1 arg2)
  • (a b &optional c d), a, b required, c, d not

optional args with default values

  • (a &optional (b 10))
  • (&optional (b 10) (c 10)), b = 10, c = 10 as default values

args can refer to previous args

  • (defun make-rectangle (width &optional (height width)) body)
  • the height which is optional is set to the width by default

Check if Args Set

  • args can check whether they were set or not, usually by convention named as -supplied-p
    • (defun foo (a b &optional (c 3 c-supplied-p)) (list a b c c-supplied-p)
    • (foo 1 2) –> ( 1 2 3 nil), c not supplied therefore c-supplied-p is nil
    • (foo 1 2 3) –> (1 2 3 t)

&rest

  • args can have a &rest just like in python
    • (defun + (& rest numbers) …)

args can be keyword args, after &optional &rest, you use &key

  • (defun foo (&key a b c) (list a b c))
  • (foo :a 1)
  • keyword arguments can have defaults
    • (defun foo (&key (a 0) (b 0 b-supplied-p)))
  • ORDER IMPORTANT &optional, &rest, &key

Return Values

  • RETURN-FROM special operator to immediately return any value from function
  • (return-from func_name (value))

Functions as Data, e.g. anonymous

  • FUNCTION provides mechanism for getting function object
  • (function func-name)
  • equivalency is #’func-name
  • funcall and apply to invoke functions by reference
  • funcall when known number of args at compile time
  • (funcall #’foo 1 2 3)
  • (defun plot (fn min max step) (loop for i from min to max by step do (loop repeat (funcall fn i) do (format t “*”)) (format t “~%”)))
  • (plot #’exp 0 5 1)
  • apply can accept list of args (apply #’plot plot-data), where plot-data is a list, can even concactenate list like this:
    • (apply #’plot plot-fn plot-data)

Anonymous Functions (lambdas)

  • (lambda (parameters) body)
  • lambda evals to a symbol representing a function
  • ((lambda (x y) (+ x y)) 2 3) ==> 5
    • e.g. (defun addtwo (x y) (+ x y)), (addtwo 2 3) ==> 5

Variables

  • lexical variable: local
  • dynamic variable: global
  • SETF general purpose assignment operator
  • dont need to declare type
  • assigning a new value changes WHAT object a variable refers to but has no effect on the previously referenced object
  • binding is a runtime manifestation of a variable
  • if you modify an arg, subsequent callers will not have a problem
  • if you pass an arg reference to a mutable object and change it, then you will induce a side effect on the original object
  • LET operator
    • (let (variable*) body-form*)
(let ((a 10) (b 1) c)
  (list a b c))
  • let creates a contextual binding, e.g. outside the let body, symbols resume normal values, this scope is called the “binding form”
  • as expected, nested bindings shadow parent bindings
  • dotimes also has lexical binding
  • let*, initial value forms for each var can refer to vars introduced earlier, much how args in a param can operate
(let* ((x 10)
       (y (+ x 10)))
  (list x y))

Lexical variables and closures

  • all binding forms introduce lexically scoped variables
  • scoped variables can be referred to only by code within the binding form
  • consider anonymous function defined within an enclosing scope
(let ((count 0)) #'(lambda () (setf count (1+ count))))
(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))
  • the anonymous function returned by the let will maintain reference to count forever
  • this is called a “CLOSURE” because it “CLOSES” over the binding created by the let
  • closures capture the binding, e.g. the ~memory address, NOT the value of the var, thus you can reassign the var as well as access the val
(let ((count 0))
  (list 
    #'(lambda () (incf count))
    #'(lambda () (decf count))))
  • globals via DEFVAR or DEFPARAMETER
(defvar *count* 0
    "count of widgets made so far")
  • defparameter always assigns the initial value to the named variable, while defvar does so only if the variable is undefined
  • defvar can also be used with no initial value, e.g. unbound
  • defvar should be used to define variables that contain data you want to keep even if you change source code using that variable
  • e.g. defvar for widget count, retains memory
  • e.g. defparameter for tolerances, changes when recompiled and reloaded
(progn 
  (defvar *count* 0)
  (defun increment-widget-count () (incf *count*))
  (increment-widget-count))
  • newer bindings shadow older bindings
(defvar *x* 10)
(defun foo () (format t "X: ~D~%" *x*))

(foo) ; will print 10
(let ((*x* 23)) (foo)) ; will print 23
  • modifications to enclosed bindings will not affect the global bindings
  • all constants are global and are defined with defconstant
(defconstant +name+ initial-value-form [ documentation-string ])
  • use plus symbols for constants
  • assign new value to binding you use setf macro
    • (setf place value)
    • (setf symbolname 5)
  • setq can change lexical and dynamic bindings
(defun foo (x) (setf x 10)) ; no effect on x outside of foo
(let ((y 20))
  (foo y)
  (print y))
  • setf can also assign multiple values at a time
(setf x 1)
(setf y 2)
(setf x 1 y 2)
(print x)
(print y)
  • Long story short, use setf whenever possible
functionpythonlisp
variablex = 10(setf x 10)
arraya[0] = 10(setf (aref a 0) 10)
hash tablehash[‘key’] = 10(setf (gethash ‘key hash) 10)
slot named field(setf (field o) 10)
  • setf’ing has no effect on previous obj in place, just like in other langs. e.g. if I rebind x to new object, the previous object does not change, except for in reference count

(incf x) === (setf x (+ x 1)) (decf x) === (setf x (- x 1)) (incf x 10) === (setf x (+ x 10))

  • incf and decf are called “modify macros”, built on top of setf and modify places by assgning a new value based on the current value
  • modify macros are smart and do not call on places twices
(incf (aref *array* (random (length *array*))))

;; naieve and wrong translation
(setf (aref *array* (random (length *array)))
    (1+ (aref *array* (random (length *array*)))))
  • naieve macros like this would be problematic, the macro is therefore programmed to be smart and avoid calling the random twice as this would return different values, REMEMBER MACROS ARE GENERATED DURING READER TIME NOT EVALUATION TIME
(aref *array* (random (length *array*)))

;; decomposes to...

(let ((tmp (random (length *array*))))
  (setf (aref *array* tmp) (1+ (aref *array* tmp))))
  • the important point here is that macros will evaluate from left to right, carefully evaluating any expressions with side effects only once to avoid problems
  • (rotatef a b) will swap the values of a and b
  • shiftf shifts values to the left
  • (shiftf a b 10) will shift b onto a, and 10 onto b

Macros: Standard Control Constructs

  • macros are a way to extend the language
  • provide automatic code generation facilities

When and unless macro

  • consider (if condition then-form [else-form])
  • can’t have multiple lisp statements in then/else
(if (spam-p current-message)
  (file-in-spam-folder current-message)
  (update-spam-database current-message))
  • wrap in progn
(if (spam-p current-message)
  (progn 
    (file-in-spam-folder current-message)
    (update-spam-database current-message)))
  • This could get quite annoying
  • macro can help expand this
(when (spam-p current-message)
  (file-in-spam-folder current-message)
  (update-spam-database current-message))
  • we could define our own macro for when

(defmacro when (condition &rest body) `(if ,condition (progn ,@body)))

(defmacro unless (condition &rest body) `(if (not ,condition) (progn ,@body)))

Cond Macro

  • issue with elifs in lisp lang

(if a (do-x) (if b (do-y) (do-z)))

  • therefore lisp provides solution in ‘cond’

(cond (test-1 form*) (test-n form*))

  • usually final clause in chain is represented with “T”

(if a (do-x) (if b (do-y) (do-z)))

can therefore be rewritten as

(cond (a (do-x)) (b (do-y)) (t (do-z)))

DO Looping Macro

  • DO
  • DOLIST
  • DOTIMES
  • LOOP macro provides mini language for expressing loops in english

(dolist (var list-form) body-form*)

(dolist (x (list 1 2 3))
  (print x))
  • DOTIMES does x number of times

(dotimes (var count-form) body-form*)

(dotimes (i 4)
  (print i))
  • DO the ultimate macro

(do (variable-definition*) (end-test-form result-form*) statement*)

  • each variable is defined in the following form

(var init-form step-form)

  • as long as end-test-form eval’s to nil, iteration continues
  • when end-test-form eval’s to true, value of last result form is returned as value of the DO expression

(do ((n 0 (1+ n)) (cur 0 next) (next 1 (+ cur next))) ((= 10 n) cur))

  • previous vars can be referred to, the step form is only calc’d at the end of exeuction of first loop, therefore (cur 0 next) can refer to next, even though it is first defined thereafter
  • the purpose of this function is find 11th fibonacci number
  • note that when n = 10, e.g. 10 iterations, it’ll return cur which contains the current fibonacci number value
(do ()
  ((> (get-universal-time) *some-future-date*))
  (format t "Waiting ~%")
  (sleep 60))

Loop Macro

  • simplest form

(loop body-form*)

(loop
  (when (> (get-universal-time) *some-future-date*)
    (return))
  (format t "Waiting~%")
  (sleep 60))
  • consider these two different ways of adding the nums 1-10 into a list
(do ((nums nil) (i 1 (1+ i)))
    ((> i 10) (nreverse nums))
(loop for i from 1 to 10 collecting i)
  • compute 11th fibonacci number
(loop for i below 10
    and a = 0 then b
    and b = 1 then (+ b a)
    finally (return a))

Macros: Defining Your Own

  • the job of a macro is not to do anything, it is to generate code that does something

(defmacro name (parameter*) “optional documentation string” body-form*)

  • to write a macro you either need to know what you expect the result to be, or where you are coming from
  • the first step therefore to writing a macro is to write one example call to the macro, and the resultant expected output

Steps for writing a macro

  1. Write a smaple call to the macro and the code it should expand to
  2. Write code that generates handwritten expansion into sample call
  3. Make sure macro abstraction does not leak

do-primes macro

  • check if number is prime
(defun primep (number)
  (when (> number 1)
    (loop for fac from 2 to
(isqrt number) never (zerop (mod number fac)))))
  • get next prime number
(defun next-prime (number)
  (loop for n from number when (primep n) return n))
  • now here is an example of using the macro

(do-primes (p 0 19) (format t “~d ” p))

  • without the macro

(do ((p (next-prime 0) (next-prime (1+ p)))) ((> p 19)) (format t “~d ” P))

  • writing the macro
  • arguments passed to the macro are lisp objects representing the source code of the macro call

(defmacro do-primes ((var start end) &body body) `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))) ((> ,var ,end)) ,@body))

  • here’s a simpler macro just for making a new printer

(defmacro printy (to-print) “prints whatever you like” `(format t “~s” ,to-print))

  • here’s a macro of printy with body expansion

(defmacro printy ((to-print) &body body) “you can print” `(progn (format t “~s” ,to-print) ,@body))

  • usage of a “,” allows expression to be eval’d
  • backquote syntax is a concise way of writing code that generates lists Backquote Syntax Equivalent List-Building Code Result `(a (+ 1 2) c) (list ‘a ‘(+ 1 2) ‘c) (a (+ 1 2) c) `(a ,(+ 1 2) c) (list ‘a (+ 1 2) ‘c) (a 3 c) `(a (list 1 2) c) (list ‘a ‘(list 1 2) ‘c) (a (list 1 2) c) `(a ,(list 1 2) c) (list ‘a (list 1 2) ‘c) (a (1 2) c) `(a ,@(list 1 2) c) (append (list ‘a) (list 1 2) (list ‘c)) (a 1 2 c)
  • (macroexpand-1 ‘(printy “lol”)) will sho expanded version of macro, you can verify if it looks right
  • in slime you can type C-c RET on open paren to see macro expansion

Practical: Building a Unit Test Framework Source

(defmacro check (form) `(report-result ,form ‘,form))

  • basic macro expanding check form

(defmacro check (&body forms) `(progn ,@(loop for f in forms collect `(report-result ,f ‘,f))))

  • This definition uses a common macro idiom of wrapping a PROGN around a series of forms in order to turn them into a single form

(defun test-+ () (check (= (+ 1 2) 3) (= (+ 1 2 3) 6) (= (+ -1 -3) -4)))

Numbers, Characters, and Strings

Basic Math Operations

(+ 1 2) ==> 3 (+ 1 2 3) ==> 6 (+ 10.0 3.0) ==> 13.0 (+ #c(1 2) #c(3 4)) ==> #c(4 6) (- 5 4) ==> 1 (- 2) ==> -2 (- 10 3 5) ==> 2 (* 2 3) ==> 6 (* 2 3 4) ==> 24 (/ 10 5) ==> 2 (/ 10 5 2) ==> 1 (/ 2 3) ==> 2/3 (/ 4) ==> 1/4

Math Library Operations

(floor) (truncate) (ceiling)

(incf x) === (setf x (1+ x)) (decf x) === (setf x (1- x)

(incf x 10) === (setf x (+ x 10)) (decf x 10) === (setf x (- x 10))

(max 10 11) –> 11 (min -12 -10) –> -12

Numeric Comparisons

(= 1 1) –> T (/= 1 1) –> NIL ; Returns true if all args diff values

(< 2 3) –> T (> 2 3) –> NIL

Character Comparison

Numeric Analog Case-Sensitive Case-Insensitive = CHAR= CHAR-EQUAL /= CHAR/= CHAR-NOT-EQUAL < CHAR< CHAR-LESSP > CHAR> CHAR-GREATERP <= CHAR<= CHAR-NOT-GREATERP >= CHAR>= CHAR-NOT-LESSP

Strings

  • Strings are a one dimensional array of characters

String Comparion

Numeric Analog Case-Sensitive Case-Insensitive = STRING= STRING-EQUAL /= STRING/= STRING-NOT-EQUAL < STRING< STRING-LESSP > STRING> STRING-GREATERP <= STRING<= STRING-NOT-GREATERP >= STRING>= STRING-NOT-LESSP

  • Have keyword arguments that can enable you to compare

substrings

– (string= “foobarbaz” “quuxbarfoo” :start1 3 :end1 6 :start2 4 :end2 7)

Collections

  • fixed size vectors can be made using (vector (count))
  • #(…) is literal notation for vectors
  • vectors can be saved and restored by PRINT and READ
  • you should use VECTOR or MAKE-ARRAY to create vectors that you plan on modifying
(make-array 5 :initial-element nil)
  • make-array can be used to make resizable vectors
  • fill pointer refers to how many elements are non nil within the vector
  • (make-array 5 :fill-pointer 0) –> length 5, no init’d values
  • to make an adjustable array, simply pass adjustable
  • to extend an adjustable array use (vector-push-extend)
(make-array 5 :fill-pointer 0 :adjustable t)

Operations on Vectors

(defparameter x (make-array 5 :fill-pointer 0)) (vector-push ‘a x) ==> 0 x ==> #(A) (vector-push ‘b x) ==> 1 x ==> #(A B) (vector-push ‘c x) ==> 2 x ==> #(A B C) (vector-pop x) ==> C x ==> #(A B) (vector-pop x) ==> B x ==> #(A) (vector-pop x) ==> A x ==> #()

Subtypes of Vector

specialized vectors can hold only of just one type if necessary, for speed in size efficiency

strings are example of specialized vector

  • you can make resizable strings by using make-array with

keyword arg :element-type ‘CHARACTER

(make-array 5 :fill-pointer 0 :adjustable t :element-type 'character)

Sequence Operations

Get the length of a vector

(defparameter x (vector 1 2 3)) (length x) ==> 3

Return an individual element by index

(elt x 0) ==> 1

  • elt actually returns a refence, so you can change the value at that index: (setf (elt x 0) 10)

General Operations

NameRequired ArgumentsReturns
COUNTItem and sequenceNumber of times item appears in sequence
FINDItem and sequenceItem or NIL
POSITIONItem and sequenceIndex into sequence or NIL
REMOVEItem and sequenceSequence with instances of item removed
SUBSTITUTENew item, item, and sequenceSequence with instances of item replaced with new item

Here are some simple examples of how to use these functions:

(count 1 #(1 2 1 2 3 1 2 3 4)) ==> 3 (remove 1 #(1 2 1 2 3 1 2 3 4)) ==> #(2 2 3 2 3 4) (remove 1 ‘(1 2 1 2 3 1 2 3 4)) ==> (2 2 3 2 3 4) (remove #\a “foobarbaz”) ==> “foobrbz” (substitute 10 1 #(1 2 1 2 3 1 2 3 4)) ==> #(10 2 10 2 3 10 2 3 4) (substitute 10 1 ‘(1 2 1 2 3 1 2 3 4)) ==> (10 2 10 2 3 10 2 3 4) (substitute #\x #\b “foobarbaz”) ==> “fooxarxaz” (find 1 #(1 2 1 2 3 1 2 3 4)) ==> 1 (find 10 #(1 2 1 2 3 1 2 3 4)) ==> NIL (position 1 #(1 2 1 2 3 1 2 3 4)) ==> 0

Sequence comparison keyword arguments

:test – pass your own comparator :key – one arg function to extract key value

:start :end – control start and end of operation on sequence :from-end – used to iterate backwards through list :count – specify how many elements removed or substituted

However, even if you pass key, find will return the whole value at that location, consider:

(find 'c #((a 10) (b 20) (c 30) (d 40)) :key #'first)

General Operations Variants

In place of item arg, take a function to be called on each element of the sequence.

(count-if #’evenp #(1 2 3 4 5)) ==> 2

(count-if-not #’evenp #(1 2 3 4 5)) ==> 3

(position-if #’digit-char-p “abcd0001”) ==> 4

(remove-if-not #’(lambda (x) (char= (elt x 0) #\f)) #(“foo” “bar” “baz” “foom”)) ==> #(“foo” “foom”)

Whole sequence Manipulations

  • copy-seq
  • reverse
  • concactenate, must be told what type of sequence to produce:

(concatenate ‘vector #(1 2 3) ‘(4 5 6)) ==> #(1 2 3 4 5 6) (concatenate ‘list #(1 2 3) ‘(4 5 6)) ==> (1 2 3 4 5 6) (concatenate ‘string “abc” ‘(#\d #\e #\f)) ==> “abcdef”

Sorting and Merging

(sort (vector “foo” “bar” “baz”) #’string<) ==> #(“bar” “baz” “foo”)

  • stable-sort guaranteed to not reorder elements considered equivalent by the predicate
  • sort onlly guarantees result is sorted

sort returns sorted output, so you must setf it

:key can be used to extract the value from a list (pass a func to key)

(merge ‘vector #(1 3 5) #(2 4 6) #’<) ==> #(1 2 3 4 5 6)

Subsequence Manipulations

(subseq “foobarbaz” 3) ==> “barbaz” (subseq “foobarbaz” 3 6) ==> “bar”

  • (FILL) can be used to initialize sequences

(position #\b “foobarbaz”) ==> 3 (search “bar” “foobarbaz”) ==> 3

  • search can search for more than one item, it searches for a sequence

(mismatch “foobarbaz” “foom”) ==> 3

  • mismatch finds where two lists first diverge

Sequence Predicates

(every #’evenp #(1 2 3 4 5)) ==> NIL (some #’evenp #(1 2 3 4 5)) ==> T (notany #’evenp #(1 2 3 4 5)) ==> NIL (notevery #’evenp #(1 2 3 4 5)) ==> T

These calls compare elements of two sequences pairwise: (every #’> #(1 2 3 4) #(5 4 3 2)) ==> NIL (some #’> #(1 2 3 4) #(5 4 3 2)) ==> T (notany #’> #(1 2 3 4) #(5 4 3 2)) ==> NIL (notevery #’> #(1 2 3 4) #(5 4 3 2)) ==> T

Sequence Mapping Functions

map maps a function a onto a sequence (map ‘vector #’* #(1 2 3 4 5) #(10 9 8 7 6)) ==> #(10 18 24 28 30)

MAP-INTO is like MAP except instead of producing a new sequence of a given type, it places the results into a sequence passed as the first argument. This sequence can be the same as one of the sequences providing values for the function. For instance, to sum several vectors–a, b, and c–into one, you could write this:

(map-into a #’+ a b c)

(reduce #’+ #(1 2 3 4 5 6 7 8 9 10)) ==> 55

Hash Tables

(defparameter h (make-hash-table))

(gethash ‘foo h) ==> NIL

(setf (gethash ‘foo h) ‘quux)

(gethash ‘foo h) ==> QUUX

Check Existence of key in hash table

(defun show-value (key hash-table) (multiple-value-bind (value present) (gethash key hash-table) (if present (format nil “Value ~a actually present.” value) (format nil “Value ~a because key not found.” value))))

(setf (gethash ‘bar h) nil) ; provide an explicit value of NIL

(show-value ‘foo h) ==> “Value QUUX actually present.” (show-value ‘bar h) ==> “Value NIL actually present.” (show-value ‘baz h) ==> “Value NIL because key not found.”

Hash Table Iteration

(maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) *h*)
  • do not add or remove when iteratating with maphash, though setf-ing is

okay

They Called It LISP for a Reason: List Processing

  • lists are composed of a primitive type called cons
  • (cons 1 2) ==> (1 . 2) In this example, car == 1, cdr == 2
  • cons are used to make singly linked lists,
  • cdrs generally point to the next element in the list
(cons 1 nil)
(cons 1 (cons 2 nil))
  • first and rest are synonyms for car and cdr
  • lists can point to lists as elements, so they can be used to represent trees of any depth/complexity
  • operations that change the value of existing objects are called destructive, since they destroy the original value
  • careful when mixing functional/non-functional as many functional forms link over data rather than truly creating it again e.g. no deep copying
  • “recycling” type destructive functions. eg. append, which reuse cons cells can only be used safely IFF the old data structures will not be used again
  • most recycling functions have non-destructive counterparts

consider NREVERSE which reuses cons cells, and REVERSE (non-destructive) which DO NOT reuse cons cells

The big difference is that I could do (reverse x) and still use x without it being reversed elsewhere. If I however typed in (nreverse x), then I would not be able to use x again as it would be backwards. Note that x is not being setf’d

  • oftentimes you’ll combine recycle with non-destructive vals for functions
(defun upto (max)
  (let ((result nil))
    (dotimes (i max)
      (push i result))
    (nreverse result)))
(setf foo (delete nil foo))
  • sort functions use recycling

List-manipulation functions

  • FIRST
  • REST
  • (NTH index list) ;; return element index of list

Mapping

  • Functional style likes to use mapping, eg. function that accepts another function to do something with said function
  • MAP
  • MAPCAR
(mapcar #'+ (list 1 2 3) (list 10 20 30))

Beyond Lists: Other Uses for Cons Cells

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment