Skip to content

Latest commit

 

History

History
381 lines (278 loc) · 8.03 KB

ch05.org

File metadata and controls

381 lines (278 loc) · 8.03 KB

defining new functions

(defun name (parameter*)
  "Optional docstring."
  body-form*)

(defun verbose-sum (x y)
  "Sum any two numbers after printing a message."
  (format t "Summing ~d and ~d.~%" x y)
  (+ x y))

optional parameters

(defun make-rectangle (width &optional (heigth width))
  ...)

(defun foo (a b &optional (c 3 c-supplied-p))
  (list a b c c-supplied-p))

rest parameters

(defun format (stream string &rest values)
  ...)

(defun + (&rest numbers)
  (loop for i in numbers summing i))

keyword parameters

(defun foo (&key a b c) (list a b c))

(foo)                               ;; => (NIL NIL NIL)
(foo :a 1)                          ;; => (1 NIL NIL)
(foo :a 2 :c 10)                    ;; => (2 NIL 10)
(foo :c :potato :b :carrot :a :yam) ;; => (:YAM :CARROT :POTATO)
(defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
  (list a b c b-supplied-p))

Now for some extra funky stuff, there is a different way to do it to have different key names on the inside and on the outside. See:

(defun foo (&key ((:apple a)) ((:box b) 0)
		   ((:charlie c) 0 c-supplied-p))
  (list a b c c-supplied-p))

(foo :apple 10 :charlie "potato" :box :banana) ;; => (10 :BANANA "potato")

mixing parameters

Usually we combine either required parameters with some other type, or sometimes we may combine &optional and &rest parameters. Combining either of those types with &key parameters can create confusing results. For instance:

(defun foo (x &optional y &key z) (list x y z))

(foo)        ;; => (NIL NIL NIL)
(foo 1)      ;; => (1 NIL NIL)
(foo 1 :z 3) ;; => ERROR since y -> :z and then there's a
	       ;; loose 3 argument with no key

Most times you’ll be better off by only using key parameters instead.

We can safely combine &rest and &key, but the behavior may be weird at first. Let’s see:

(defun foo (&rest rest &key a b c)
  (list rest a b c))

(foo :a 1 :b 2 :c 3) ;; => ((:A 1 :B 2 :C 3) 1 2 3)

function return values

return-from is common to blocks, not only functions. Otherwise, the last expression’s return value is returned by the function, normally.

 (defun foo (n)
   (dotimes (i 10)
     (dotimes (j 10)
	(when (> (* i j) n)
	  (return-from foo (list i j))))))

higher-order functions

(defun foo (x) (* 2 x))
(function foo) ;; => #<Interpreted Function FOO>
#'foo          ;; => #<Interpreted Function FOO>

FUNCALL is to be used when the number of arguments is known at time of writing. Hence:

(foo 1 2 3) ;; is equivalent to
(funcall #'foo 1 2 3)

However, take notice:

(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 4 1/2)

Now let’s suppose the arguments for PLOT came as a list, in a variable called PLOT-DATA.

(plot (car plot-data) (cadr plot-data) (caddr plot-data) (cadddr plot-data))
(plot (first plot-data) (second plot-data) (third plot-data) (fourth plot-data))

This is clearly disgusting.

(apply #'plot plot-data)
(apply #'plot #'exp plot-data) ;; mu'isu'a the function isn't inside the list 

anonymous functions

(lambda (params) body)

(funcall #'(lambda (x y) (+ x y)) 2 3)
((lambda (x y) (+ x y)) 2 3)

variables

basics

(defun foo (x y z) (+ x y z))

(let (variable*)
  body-form*)

(let ((x 10) (y 20) z)
  ...)

(dotimes (x 10) (format t "~d " x))

(let* ((x 10)
	 (y (+ x 20)))
  (list x y))
;; is equivalent to
(let ((x 10))
  (let ((y (+ x 20)))
    (list x y)))

lexical variables and closures

(let ((count 0))
  #'(lambda () (setf count (1+ count))))

(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))

(funcall *fn*) ;; => 1
(funcall *fn*) ;; => 2
(funcall *fn*) ;; => 3

(let ((count))
  (list
   #'(lambda () (incf count))
   #'(lambda () (decf count))
   #'(lambda () count)))

dynamic variables

(defvar *count* 0
  "Count of widgets made so far.")

(defparameter *gap-tolerance* 0.001
  "Tolerance to be allowed in widget gaps.")
(defun increment-widget-count () (incf *count*))

LET can shadow global bindings.

(let ((*standard-output* *some-other-stream*))
  (stuff))

Once stuff returns and control leaves the LET, standard-output will go back to referring to its previous binding.

(defvar *x* 10)
(defun foo () (format t "X: ~d~%" *x*))

(foo) ;; => "X: 10\n" NIL

(let ((*x* 20)) (foo)) ;; => "X: 20\n" NIL

(foo) ;; => "X: 10\n" NIL

(defun bar ()
  (foo)
  (let ((*x* 20)) (foo))
  (foo))

constants

(defconstant +a-name+ initial-value-form "docstring")

assignment

(setf place value)

(setf x 10)

(defun foo (x) (setf x 10)) ;; has no effect on the outside of foo

(let ((y 20))
  (foo y)
  (print y)) ;; prints 20

(setf x 10 y 20) ;; does what you'd think

(setf x (setf y (random 10))) ;; SETF returns the assigned value

generalized assignment

(setf x 10)
(setf (aref a 0) 10)
(setf (gethash 'key hash) 10)
(setf (field o) 10)
(incf x)
(decf x)
(incf x 10)

(rotatef a b) ;; equivalent to
(let ((tmp a)) (setf a b b tmp) nil)

(shiftf a b 10) ;; equivalent to
(let ((tmp a)) (setf a b b 10) tmp)

macros: standard control constructs

WHEN and UNLESS

(if condition then-form &optional else)

(when (spam-p current-message)
  (file-in-spam-folder current-message)
  (update-spam-database current-message))

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

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

COND

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

AND and OR

AND and OR are macros in order to be able to short-circuit.

(not nil)
(not (= 1 1))
(and (= 1 2) (this-will-not-run))
(or (= 1 1) (neither-will-this))

looping

Default constructs: DO, DOTIMES, DOLIST and LOOP. Refer to the cookbook for more memes. Options of packages:

  • iterate
  • for
  • series
  • gtwiwtg

MAP, MAPCAR and etc can also be used for related stuff.

DOTIMES and DOLIST

 (dolist (var list)
   body-form*)
 
 (dolist (x '(1 2 3))
   (print x)
   (if (evenp x)
	(return)))
 
 (dotimes (var count)
   body-form*)
 
 (dotimes (x 20)
   (dotimes (y 20)
     (format t "~3d " (* (1+ x) (1+ y))))
   (format t "~%"))

DO

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

;; a variable-definition looks like:
(var init-form step-form)

At the beginning of each iteration, once the variables have all been given their new values, end-test-form is evaluated. As long as it evaluates to NIL, the iteration proceeds, running the statements in order.

When end-test-form evaluates to T, the result-forms are evaluated, and the value of the last one is returned as the value of the DO expr.

;; fibonacci example
(do ((n 0 (1+ n))
     (cur 0 next)
     (next 1 (+ cur next)))
    ((= 10 n) cur))

;; dotimes-like
(do ((i 0 (1+ i)))
    ((>= i 4))
  (print i))

;; idiomatic ofc would be
(dotimes (i 4) (print i))

practical chapter