Functions
8.1 Defining Functions
(defun square (x)
"Return the square of X." ; docstring
(* x x))
(defun hypotenuse (a b)
(sqrt (+ (square a) (square b))))
(hypotenuse 3 4) ; 5.0
(documentation 'square 'function) ; "Return the square of X."8.2 Lambda Lists: Optional, Rest, Keyword and Aux Parameters
Order: required → &optional → &rest → &key → &aux. Avoid mixing &optional and &key in one lambda list (it's confusing).
(defun greet (name &optional (greeting "Hello") (punct "!" punct-supplied-p))
(format nil "~a, ~a~a~@[ (custom)~*~]" greeting name punct punct-supplied-p))
(greet "Ana") ; "Hello, Ana!"
(greet "Ana" "Hi" "?") ; "Hi, Ana? (custom)"
(defun sum-all (&rest numbers)
(reduce #'+ numbers))
(sum-all 1 2 3 4) ; 10
(defun make-window (&key (width 800) (height 600) (title "Untitled") fullscreen)
(list :title title :size (list width height) :fullscreen fullscreen))
(make-window :title "Game" :width 1024)
;; (:TITLE "Game" :SIZE (1024 600) :FULLSCREEN NIL)
(defun log-msg (level &rest args &key (stream *standard-output*) &allow-other-keys)
(format stream "[~a] ~s~%" level args))
(defun area (radius &aux (r2 (* radius radius))) ; &aux: local variables in the lambda list
(* pi r2))8.3 Functions as Values
#'square ; the function object (Lisp-2!)
(function square) ; same
(funcall #'square 5) ; 25
(apply #'+ 1 2 '(3 4)) ; 10: the last argument is a list
(mapcar #'square '(1 2 3)) ; (1 4 9)
(funcall (lambda (x) (* 2 x)) 21) ; 42
(defparameter *op* #'+)
(funcall *op* 1 2) ; 3
(setf (symbol-function 'double) (lambda (x) (* 2 x))) ; install a function at runtime
(double 21) ; 42
(fboundp 'double) ; T8.4 Local Functions: flet and labels
(defun process (items)
(flet ((clean (s) (string-trim " " s))) ; non-recursive local function
(mapcar #'clean items)))
(defun factorial (n)
(labels ((fact-iter (i acc) ; labels allows recursion and mutual reference
(if (<= i 1) acc (fact-iter (1- i) (* acc i)))))
(fact-iter n 1)))
(factorial 20) ; 24329020081766400008.5 Recursion
The standard doesn't require tail-call elimination, but SBCL performs it with default optimization settings ((debug 2) or lower). For guaranteed iteration, use loop/do.
(defun fib (n)
(if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(defun my-length (list)
(if (null list) 0 (1+ (my-length (rest list)))))
(defun my-reverse (list &optional acc)
(if (null list) acc (my-reverse (rest list) (cons (first list) acc))))