Lisp syntax sheet: quiz 3 and midterm
; global variable, constant declaration/initialization
(defvar x 10)
(defconstant pi 3.14)

; simple data types and syntax:
;    real numbers, e.g. 5.123
;    integers, e.g. 1024
;    rationals, e.g. 14/3
;    strings, e.g. "foo"
;    characters, e.g. #\c
;    booleans, e.g. t and nil

; prefix style function calls and operations, e.g.
;       (+ (* x y) (- (/ a b) c))
; equivalent of C++ (x*y) + ((a/b) - c)
;
; common math functions:
;    +, -, *, /, tan sin cos sqrt mod ceiling floor expt
; e.g. (expt 2 3) is 2 to the power of 3

; typechecking functions return t (true) if the parameter
;    has the specified type, nil (false) otherwise
; (stringp x) (integerp x) (realp x) (numberp x) (listp x)
; (characterp x) (symbolp x) (functionp x) etc

; function to read and return next item of user input
; (can be number, string, character, list, etc)
(read)

; format can display text, variable, newline (~%), returns nil
(format t "Value in x is ~A~%" x)

; if/else statement to return larger of x and y
;    (skips error checking for numbers in this example)
(if (> x y) x y)

; cond statements take a list of pairs, each has
;    a condition to check and value to return if true
;    t used as final pair's condition since it is always true
(cond
    ((stringp z) (format t "length of string ~A is ~A~%" z (length z)))
    ((listp z) (format t "length of list ~A is ~A~%" z (length z)))
    ((numberp z) (format t "given number, value is ~A~%" z))
    (t (format t "given unexpected value: ~A~%" z)))

; numeric comparison functions: < <= > >= = /=
; string comparisons: string= string> etc
; boolean operators: and or not

; sequence functions (work on lists)
(length L)     ; returns length of list L
(member e L)   ; true if e is element of L, false otherwise
(count e L)    ; counts how often e is in L
(position e L) ; finds first position of e in L

; list syntax '(1 2 3 4) where elements can be any type
; empty list denoted nil or '()
(list x y z)   ; create/return list with elements x, y, z
(dolist (e L) (format t "Next element: ~A~%" e)) ; do something with each element of L
(null L)       ; true iff L is empty list
; more list functions
(car L)        ; returns first element of L
(cdr L)        ; returns list of all the elements in L except the first
(cons e L)     ; return a list with element e followed by elements of L
(reverse L)    ; returned reversed copy of L
(append L1 L2) ; return list with elements of L1 then elements of L2

; examples of higher order functions/calls
(sort '(10 4 1.2 9.5 8) '<=) ; final argument is the comparison function to be used
(funcall 'cons "foo" '(1 2 3)) ; like running (cons "foo" '(1 2 3))
(apply 'cons '("foo" '(1 2 3))) ; also like running (cons "foo" '(1 2 3))
(eval '(cons "foo" '(1 2 3))) ; and also like running (cons "foo" '(1 2 3))
(maplist 'length '(10 20 30)) ; returns '(3 2 1)
(mapcar 'sqrt '(16 4 9)) ; returns '(4 2 3)
(reduce '/ '(120 3 5 2)) ; => 120/3 = 40, then 40/5 = 8, then 8/2 => 4, returns 4
(map 'list '* '(10 20 30) '(5 2 3)) ; => applies * pairwise, giving '(50 40 90)

; example of tail-recursive factorial with an accumulator
(defun factorial (N &optional (sofar 1))
   (cond
      ((not (integerp N)) nil)
      ((< N 1) 0)
      ((= N 1) sofar)
      (t (factorial (- N 1) (* N sofar)))))

; sample let-over-(labels-over-)lambda for a stored integer
;    with sample local functions
(defun buildStoredInt (&optional (orig 0))
   (let  ( ; local variables
           (stored 0)  ; just a valid stored integer, defaults to 0
         )
      (labels
         ( ; local functions
            (setNew (v) (if (integerp v) (setf stored v))) ; sets stored if v a valid int
            (incr () (setf stored (+ stored 1))) ; increments stored value
            (decr () (setf stored (- stored 1))) ; decrements stored value
            (disp () (format t "~A~%" stored))   ; displays stored value
         )
         (setNew orig) ; try to initialize stored from orig
         ; build and return dispatcher as lambda function
         (lambda (cmd &optional (val 0))
            (cond
                ; run whichever local function corresponds to the caller's command
                ((equalp cmd 'increment) (incr))
                ((equalp cmd 'decrement) (decr))
                ((equalp cmd 'display) (disp))
                ((equalp cmd 'set) (setnew val))
                (t (format t "Error: invalid command ~A~%" cmd)))))))

; calls to buildStoredInt
(defvar x (buildStoredInt)) ; build dispatcher using orig's default value of 0 for stored
(defvar y (buildStoredInt 15)) ; build dispatcher using value 15 for stored
(defvar z (buildStoredInt "foo")) ; build dispatcher, setting "foo" fails, stored still 0

; uses of resulting dispatchers (calls to the lambdas to update content)
(funcall x 'increment) ; increments x's stored value (to 1)
(funcall y 'display)   ; displays y's stored value (15)
(funcall x 'decrement) ; decrements x's stored value (back down to 0)a
(funcall z 'set 5)     ; set z's stored value (to 5)

; optional parameters with default values (&optional)
(defun f (w x &optional (y nil) (z 99))
   ; w and x are required, y and z are optional
   )
(f 1 2 3) ; uses 3 for y, the default 99 for z

; optional keyword parameters (&key)
(defun f (x &optional &key (y nil) (z 99))
   ; x required, y/z optional but must be named to override
   (format t "x: ~A, y: ~A, z: ~A~%" x y z))
(f 10)       ; x: 10, y: NIL, z: 99
(f 10 :y 17) ; x: 10, y: 17, z: 99
(f 1 :z 3)   ; x: 1, y: NIL, z: 3

; variadic functions (&rest)
(defun f (x &rest others)
   ; x required, can be 0 or more additional
   (format t "x: ~A, others: ~A~%" x others)
   ; for recursive call, use apply to embed content as args
   (if (not (null others)) (apply 'f others)))
; (f 1 2 3 4) produces output:
; x: 1, others: (2 3 4)
; x: 2, others: (3 4)
; x: 3, others: (4)
; x: 4, others: NIL

; returning multiple values from a function
(defun f (a b c)
   ; return 3 separate values
   (values (* a b) (* a c) (* b c)))

; capturing just the i'th return value
(setf result (nth-value i (f 10 20 30)))

; capturing all three return values using multiple-value-bind
(multiple-value-bind
   (res1 res2 res3) ; local variables to store the returns
   (f 10 20 30) ; function call generating the results
   ; ...
   ; ... statements using res1, res2, res3
   )

; sample swap macro using gensym to create unique name
(defmacro swap (a b)
   (let ; store the generated name in a variable
        ((localname (gensym)))
        ; transformed code uses the generated name
        `(let ((,localname ,a))
              (,a ,b)
              (,b ,localname))))

; sample recursive macro (variadic)
(defmacro printArgs (a &rest others)
   (if (null others)
       `(format t "~A~%" ,a) ; simple if a is only arg
       ; otherwise more complex
       `(block ,(gensym)
           (format t "~A~%" ,a)
           (printArgs ,@others))))
; (printArgs 1) => (format t "~A~%" 1)
; (printArgs 1 2) => (block G385 (format t "~A~%" 1)
;                      (printArgs 2))
; where the printArgs 2 is then expanded into a format
; symbols and properties
(symbolp s) ; does var/param s contain a symbol?
(boundp s)  ; is the symbol in s bound to a global var?
(fboundp s) ; is it bound to a global function?
(symbol-plist s) ; get a list of the symbol properties
(get s p) ; look up the value of a property for a symbol
(setf (get s p) v) ; set a property value
(remprop s p) ; remove a property/value pair


; structure of a generic 'do' loop
(do  ( ; list of local vars (name initval update)
       (x 1 (+ x 1))
       (y "?" (read))
     )

     ( (> x 10) ; stopping condition
       ; ... actions to take
       ; ... after stopping
       ; ... last one is return value
     )

     ; ... body of
     ; ... loop
)

; hash tables (lookup table for key/value pairs)

(defvar HT (make-hash-table)) ; create empty table
(gethash k HT) ; get value associated with key k
(set (gethash k HT) v) ; set value associated with k
(remprop k HT) ; remove key/value pair for key k
(hash-table-count HT) ; return # of key-value pairs
(maphash 'f HT) ; run function f on all key value pairs
   ; f must expect a key and value as its two params

; process all the keys (order of processing unpredictable)
(loop for k being the hash-keys of HT do
    ; ... some action with the current key
    )

; loops to create list of all keys, all values
(loop for k being the hash-keys of HT collect k)
(loop for k being the hash-keys of HT collect (get-hash k HT))

; structures example

; define the name/fields of a struct
(defstruct circle x y radius)

; defstruct creates the circle functions for us:
(defvar c (make-circle)) ; create and return a new circle
(circle-x c) ; return field x
(circle-y c) ; return field y
(circle-radius c) ; return field radius
(setf (circle-x c) 27) ; the fields are all setable, e.g.
(circle-p c) ; is c a circle?
(copy-circle c) ; return a deep copy

; creating dynamically-scoped variable
(let ((a 1)(b 2)(c 3)) ; local vars
     (declare (special b)) ; make b dynamic
     ;... b is dynamically scoped thru everything called
     )