When talking about Lisp-like languages there is a common distinction between what is known as a Lisp-1 and a Lisp-2. In a Lisp-1, symbols only have a value and if a symbol refers to a function then the value of that symbol will be that function. In a Lisp-2, symbols can have separate associated values and functions and so a special form is required to refer to the function stored in a symbol instead of the value.
Common Lisp is basically a Lisp-2 however there are in fact more than 2 namespaces (things that symbols can refer to) -- symbols can refer to values, functions, types and tags, for example.
Functions in Common Lisp are first class values. An anonymous function can be created by using lambda
. For example, here is a function of 3 arguments which we then call using funcall
CL-USER> (lambda (a b c) (+ a (* b c)))
#<FUNCTION (LAMBDA (A B C)) {10034F484B}>
CL-USER> (defvar *foo* (lambda (a b c) (+ a (* b c))))
*FOO*
CL-USER> (funcall *foo* 1 2 3)
7
Anonymous functions can also be used directly. Common Lisp provides a syntax for it.
((lambda (a b c) (+ a (* b c))) ; the lambda expression as the first
; element in a form
1 2 3) ; followed by the arguments
Anonymous functions can also be stored as global functions:
(let ((a-function (lambda (a b c) (+ a (* b c))))) ; our anonymous function
(setf (symbol-function 'some-function) a-function)) ; storing it
(some-function 1 2 3) ; calling it with the name
Quoted lambda expressions are not functions
Note that quoted lambda expressions are not functions in Common Lisp. This does not work:
(funcall '(lambda (x) x)
42)
To convert a quoted lambda expression to a function use coerce
, eval
or funcall
:
CL-USER > (coerce '(lambda (x) x) 'function)
#<anonymous interpreted function 4060000A7C>
CL-USER > (eval '(lambda (x) x))
#<anonymous interpreted function 4060000B9C>
CL-USER > (compile nil '(lambda (x) x))
#<Function 17 4060000CCC>
Any symbol in Common Lisp has a slot for a variable to be bound and a separate slot for a function to be bound.
Note that the naming in this example is only for illustration. Global variables should not be named foo
, but *foo*
. The latter notation is a convention to make it clear that the variable is a special variable using dynamic binding.
CL-USER> (boundp 'foo) ;is FOO defined as a variable?
NIL
CL-USER> (defvar foo 7)
FOO
CL-USER> (boundp 'foo)
T
CL-USER> foo
7
CL-USER> (symbol-value 'foo)
7
CL-USER> (fboundp 'foo) ;is FOO defined as a function?
NIL
CL-USER> (defun foo (x y) (+ (* x x) (* y y)))
FOO
CL-USER> (fboundp 'foo)
T
CL-USER> foo
7
CL-USER> (symbol-function 'foo)
#<FUNCTION FOO>
CL-USER> (function foo)
#<FUNCTION FOO>
CL-USER> (equalp (quote #'foo) (quote (function foo)))
T
CL-USER> (eq (symbol-function 'foo) #'foo)
T
CL-USER> (foo 4 3)
25
CL-USER> (funcall foo 4 3)
;get an error: 7 is not a function
CL-USER> (funcall #'foo 4 3)
25
CL-USER> (defvar bar #'foo)
BAR
CL-USER> bar
#<FUNCTION FOO>
CL-USER> (funcall bar 4 3)
25
CL-USER> #'+
#<FUNCTION +>
CL-USER> (funcall #'+ 2 3)
5
Common Lisp contains many higher order functions which are passed functions for arguments and call them. Perhaps the most fundamental are funcall
and apply
:
CL-USER> (list 1 2 3)
(1 2 3)
CL-USER> (funcall #'list 1 2 3)
(1 2 3)
CL-USER> (funcall #'list 1 2 3 4 5)
(1 2 3 4 5)
CL-USER> (apply #'list '(1 2 3))
(1 2 3)
CL-USER> (apply #'list 1 2 '(4 5))
(1 2 3 4 5)
CL-USER> (apply #'+ 1 (list 2 3))
6
CL-USER> (defun my-funcall (function &rest args)
(apply function args))
MY-FUNCALL
CL-USER> (my-funcall #'list 1 2 3)
(1 2 3)
There are many other higher order-function which, for example, apply a function many times to elements of a list.
CL-USER> (map 'list #'/ '(1 2 3 4))
(1 1/2 1/3 1/4)
CL-USER> (map 'vector #'+ '(1 2 3 4 5) #(5 4 3 2 10))
#(6 6 6 6 15)
CL-USER> (reduce #'+ '(1 2 3 4 5))
15
CL-USER> (remove-if #'evenp '(1 2 3 4 5))
(1 3 5)
The reduce function can be used to sum the elements in a list.
(reduce '+ '(1 2 3 4))
;;=> 10
By default, reduce performs a left-associative reduction, meaning that the sum 10 is computed as
(+ (+ (+ 1 2) 3) 4)
The first two elements are summed first, and then that result (3) is added to the next element (3) to produce 6, which is in turn added to 4, to produce the final result.
This is safer than using apply (e.g., in (apply '+ '(1 2 3 4)) because the length of the argument list that can be passed to apply is limited (see call-arguments-limit), and reduce will work with functions that only take two arguments.
By specifying the from-end keyword argument, reduce will process the list in the other direction, which means that the sum is computed in the reverse order. That is
(reduce '+ (1 2 3 4) :from-end t)
;;=> 10
is computing
(+ 1 (+ 2 (+ 3 4)))
Common Lisp already has a reverse function, but if it didn't, then it could be implemented easily using reduce. Given a list like
(1 2 3) === (cons 1 (cons 2 (cons 3 '())))
the reversed list is
(cons 3 (cons 2 (cons 1 '()))) === (3 2 1)
That may not be an obvious use of reduce, but if we have a "reversed cons" function, say xcons, such that
(xcons 1 2) === (2 . 1)
Then
(xcons (xcons (xcons () 1) 2) 3)
which is a reduction.
(reduce (lambda (x y)
(cons y x))
'(1 2 3 4)
:initial-value '())
;=> (4 3 2 1)
Common Lisp has another useful function, revappend, which is a combination of reverse and append. Conceptually, it reverses a list and appends it to some tail:
(revappend '(3 2 1) '(4 5 6))
;;=> (1 2 3 4 5 6)
This can also be implemented with reduce. It fact, it's the same as the implementation of reverse above, except that the initial-value would need to be (4 5 6) instead of the empty list.
(reduce (lambda (x y)
(cons y x))
'(3 2 1)
:initial-value '(4 5 6))
;=> (1 2 3 4 5 6)
Functions remember the lexical scope they where defined in. Because of this, we can enclose a lambda in a let to define closures.
(defvar *counter* (let ((count 0))
(lambda () (incf count))))
(funcall *counter*) ;; => 1
(funcall *counter*) ;; = 2
In the example above, the counter variable is only accessible to the anonymous function. This is more clearly seen in the following example
(defvar *counter-1* (make-counter))
(defvar *counter-2* (make-counter))
(funcall *counter-1*) ;; => 1
(funcall *counter-1*) ;; => 2
(funcall *counter-2*) ;; => 1
(funcall *counter-1*) ;; => 3
A simple example:
CL-USER> (defun make-apply-twice (fun)
"return a new function that applies twice the function`fun' to its argument"
(lambda (x)
(funcall fun (funcall fun x))))
MAKE-APPLY-TWICE
CL-USER> (funcall (make-apply-twice #'1+) 3)
5
CL-USER> (let ((pow4 (make-apply-twice (lambda (x) (* x x)))))
(funcall pow4 3))
81
The classical example of function composition: (f ∘ g ∘ h)(x) = f (g (h (x)):
CL-USER> (defun compose (&rest funs)
"return a new function obtained by the functional compositions of the parameters"
(if (null funs)
#'identity
(let ((rest-funs (apply #'compose (rest funs))))
(lambda (x) (funcall (first funs) (funcall rest-funs x))))))
COMPOSE
CL-USER> (defun square (x) (* x x))
SQUARE
CL-USER> (funcall (compose #'square #'1+ #'square) 3)
100 ;; => equivalent to (square (1+ (square 3)))
Parameter | Details |
---|---|
name | some (unevaluated) symbol which names a function |
symbol | a symbol |
function | a function which is to be called |
args... | zero or more arguments (not a list of arguments) |
arglist | a list containing arguments to be passed to a function |
arg1, arg2, ..., argn | each is a single argument to be passed to a function |