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)                      ; T

8.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)                                     ; 2432902008176640000

8.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))))