Title: Advanced Functions
1Advanced Functions
- In CL, functions are often supplied as parameters
to other functions - This gives us tremendous flexibility in writing
functions whose specific behavior will be
determined later when someone else (or you)
supplies a function - functions can be passed as parameters
- functions can be specified as arguments
- functions can be applied to a given list of data
to use the results of that application - Here, we consider how to do these various things
- what permits this is that a function in CL is
treated like any other data type and is pointed
to by a pointer - to denote that something is a function, it is
preceded by as in evenp or gt or length - the basic REPL function eval, can be applied at
any time - similarly, alternative functions exist to apply a
function to data (mapping functions, funcall,
apply, etc)
2Example
- Consider that you want to write a plotting
function that, given values x and y, will plot
the values f(i) for i from x to y - This is easy to write if you already know f
- The problem with this is that plot only works for
function f
(defun plot (x y) (let (temp) used to
store f(i) (do ((i x ( i 1)))
iterate for i from x to y ((gt i y))
(setf temp (f i)) temp f(i)
(dotimes (a temp) do
f(i) times (format t
)) print an (format
t )))) start a new line afterward
3Solution Pass the Function
- Here, we pass the function that we want to apply
as a parameter - To apply the function to a value, i in this case,
we use funcall
We call plotf passing it functionname and x and
y as in (plotf add_1 5 10) if we have a
function called add_1, or (plotf square 5
10) if we have a function called square
(defun plotf (f x y) (let (temp)
(do ((i x ( i 1))) ((gt i y))
(setf temp (funcall f i))
(dotimes (a temp) (format t ""))
(format t ""))))
4Another Approach Apply
- Aside from using funcall, which expects as params
a function and parameter(s), you can use apply - Apply applies a function to a list of parameters
where the function can be any of - a compiled function
- (apply max (3 5 4 2)) ? 5
- a lambda-expression
- (apply (lambda (x y) (if (gt x 0) y ( y -1)))
(0 3)) ? -3 - a symbol (which could be stored in a variable or
provided in the apply statement) - (setf f )
- (apply f (1 2)) ? 3
- Note since apply expects the parameter(s) to be
in a list, if the function to be applied is one
that expects a single atom as a parameter, it
still must be placed into a list - (apply numberp (list x)) is correct
- (apply numberp x) is incorrect
5Lambda Functions
- A lambda function is a nameless function
- Having a function with no name seems useless
- how can you use it?
- There are occasions where you will need a
function one time to be applied right away - you can define a lambda function for this purpose
- There are numerous functions that apply functions
- mapcar for instance
- Lambda functions are written using the following
notation - (lambda (params) body)
- As in (lambda (x) ( x 1)) ? this function will
return 1 greater than whatever the argument is - you will use the defined lambda function inside
of another function, such as in apply or mapcar,
and there are other circumstances as we will see
later
6Mapping Revisited
- Recall the mapping functions
- They apply a function to a list and return a new
list - mapcar apply the car of the list to the
function and then cons the result to (mapcar
function (cdr lis)) - We specify the function as name or (lambda
expression) - (mapcar numberp lis) ? (list of items where T
corresponds to those that are numbers and nil to
those that are not) - Here is an example of using mapcar with a lambda
expression - (mapcar (lambda (x) (if (gt x 0) x ( x -1)))
lis) ? this returns the list of (hopefully
numbers) after applying absolute value - alternatively, we can achieve the same by doing
- (mapcar abs lis)
- Now that weve seen the basics of applying
functions, we reconsider some of the functions
covered earlier in the semester and see that they
are far more flexible than we originally thought
7Functions that Apply Functions
- Many of the sequence functions can take functions
as additional parameters - Recall count
- (count a b) counts the number of times a occurs
in b - (count 5 (3 5 7 8 4 6 2)) ? 1
- count also has a parameter called test which can
be supplied with a function, if true when applied
to the parameter, then count counts that
parameter - (count a b test gt) counts the number of
times that a is greater than each element in b - (count 5 (3 5 7 8 4 6 2) test gt) ? 3
- (count 5 (3 5 7 8 4 6 2) test gt) ? 4
- Similarly, remove, position, find, delete (the
destructive version of remove), replace and subst
can all take test arguments
8-if and -if-not Functions
- The same functions have variants that end with
-if and -if-not - (count-if function lis)
- Count the number of elements of lis that, when
applied to the function, are t - (count-if numberp (3 3.3 a \a t)) ? 2
- (count-if-not evenp (6 3 7 2 9 1)) ? 4
- 4 of the items are not t when evenp is applied
- Functions that have -if and -if-not variants
- member, find, replace, position, delete, subst,
assoc, rassoc - also stable-sort and merge
- Example lis is a list of various atoms
(numbers, symbols, characters, t, nil) and we
want to return the list that constitutes only
numbers - (remove-if-not numberp lis) non-destructive,
returns new list - (delete-if-not numberp lis) destructive
version, lis is altered - Note in this latter version, we would no longer
want lis because it still may contain non-numbers - Notice we do not supply an argument to be
compared, only a test - (count 5 lis) ? counts number of 5s in lis but
(count-if equal-to-five lis) ? uses a function
to compare the elements of lis
9Examples
(remove-if numberp (a b 3 c 3.3 d 1/3 e 11
nil 10)) ? (a b c d e 11 nil) (remove-if-not
numberp (a b 3 c 3.3 d 1/3 e 11 nil 10))
? (3 3.3 1/3 10) Notice that we cannot use
(remove lis test numberp) here this lacks a
parameter the element to be removed (remove-if
(lambda (x) (not (numberp x))) (a b 3 c 3.3
d 1/3 e 11 nil 10)) ? (3 3.3 1/3
10) (count-if (lambda (x) (equal x 5)) (a 5 b
5 c 3 d 55)) (position-if zerop (1 2 4 12 0
16 17)) ? 4 (member-if listp (a b c d)) ?
nil (member-if listp (a (b c) (d e f) g (h)))
?((b c) (d e f) g (h)) The functions supplied in
these examples are predicate functions that use a
list element as a parameter, we dont have the
ability to do something like (count-if gt ())
because we cant specify the value we want to
test gt against yet
10test, test-not, count and key
- In addition to adding if or if-not, many of
these sequence functions can take test and
test-not parameters - The sequence function is applied to the element
of the sequence if the test result is non-nil
(for test) or nil (for test-not) - (count 3 (3 6 2 1 7 4) test gt) ? 3 (number of
elements gt 3) - The count parameter is used to limit the number
of elements in a sequence that are examined by
the function being applied - this can also be used with from-end t to work
backwards - (remove 3 (1 2 3 4 3 5 3 6 3) count 2) ? (1 2 4
5 3 6 3) - (remove 3 (1 2 3 4 3 5 3 6 3) count 2 from-end
t) ? (1 2 3 4 3 5 6) - The key parameter allows you to supply another
function to apply to the parameter prior to
applying the main function - For instance, if your list is really a list of
lists, you can supply key car so that your test
applies only to the car of each sublist - If lis is ((csc 375) (cit 140) (eng 200) (csc
402) (mat 385)) then we can count the number of
csc coursers as - (count csc lis key car)
11Additional Examples
- Let lis1 be (3 6 2 1 7 4)
- And lis2 be ((a 3) (b 4) (5 c) (6 d) (7 2) (e 8)
(3 f)) - (member 3 lis test gt) ? (2 1 7 4)
- returns the member of the first item where 3 gt
item (that is, the first item smaller than 3) - (remove 3 (3 6 2 1 7 4) test lt) ? (3 2 1)
- returns the list where every number in which 3 lt
is true is removed (so 6, 7 and 4 are removed
since 3 lt them) - (member-if-not numberp lis2 key cadr) ? ((5
C) (6 D) (7 2) (E 8) (3 F)) - (remove 3 lis2 key car) ? ((A 3) (B 4) (5 C)
(6 D) (7 2) (E 8)) - (remove-if-not oddp lis1) ? (3 1 7)
- What if I want to remove two of the top-level
elements from the rear of lis2 where the second
element gt 4? - (remove 4 lis2 test gt from-end t key cadr)
? this yields an error - cant apply gt to the letter symbols (such as (6
d)) so instead, lets try this - (remove-if (lambda (x) (and (numberp x) (gt x
4))) lis2 count 2 from-end t key cadr) ? ((A
3) (B 4) (5 C) (6 D) (7 2) (3 F))
12Sorting
- CL has a built-in sort function which must be
supplied the sequence to sort and a function to
apply (the test used in sorting such as gt) - Form (sort sequence function)
- (sort (5 3 6 2 9 8 1 7) gt) ? (9 8 7 6 5 3 2 1)
- What if I have a list of lists like lis2
previously where each sublist contains an atom
and a number in that order? - (sort ((c 5) (b 3) (d 4) (a 6) (e 1) (f 2)) gt
key cadr) ? ((A 6) (C 5) (D 4) (B 3) (F 2) (E
1)) - Note sort is destructive, so that (sort lis1
lt) from the previous slide will return (1 2 3 4
6 7) and lis1 will now be (1 2 3 4 6 7) - Stable-sort is a variation of sort that is
guaranteed to not reorder elements that are equal
(this could happen for instance if we had a list
like ((c 5) (d 4) (a 5) (b 2) (e 4)) depending on
how sort is implemented
13Sequence Predicates
- Just as sequences have these functions that take
a function as an argument, there are also
sequence predicate functions - Each of these takes a function and a sequence and
applies the function to each list element until
either the predicate has been determined
(short-circuiting causes it to stop at this
point) or it has reached the end of the sequence - The sequence predicates are
- every all elements of the sequence must pass
the test to return t, else nil - some at least 1 element must pass to return t,
else nil - notany all elements must fail the test to
return t, else nil - notevery at least 1 element must fail to return
t, else nil - (every plusp lis)
- returns t only if every element of lis is a
positive number, nil otherwise (or error if not
element element of lis can be applied to plusp) - (if (notany minusp lis) (mapcar sqrt lis)
(format t error in taking square root)) - if notany returns t then mapcar proceeds,
otherwise we send an output message
14Applying Functions To Multiple Items
- The predicate functions, mapcar, and others can
apply to multiple sequences when supplied with a
function that takes multiple arguments - Consider that a is the list (1 2 3) and b is the
list (2 4 6) - (every gt b a) ? compares 2 to 1, 4 to 2, 6 to 3
and returns t - (some a b) ? nil since no pair is equal (1,
2 2, 4 3, 6) - (mapcar a b) ? (3 6 9)
- (mapcar (remove 'evenp a) (remove 4 b)) ? (3
8) - Notice here that we run the risk of having lists
of two different lengths which will work, but may
not be what we expect - We also run the risk of a and/or b containing
non-numbers - (if (and (every numberp (append a b)) (
(length a) (length b))) (mapcar a b)) - This fixes both of the previous problems
15Map and Map-Into
- These are variations of the mapping functions
- Map is just like mapcar but takes an additional
parameter of the type of sequence to create - (map list (1 2 3 4) (2 4 6 8)) ? (3 6 9
12) - Notice how map, like other sequence operations,
can take data of one form (a vector here) and
translate it into another (a list) - (map string char-upper (\a \b \! \3 \c))
? AB!3C - Map-into is destructive in that it performs the
mapping, but rather than returning a new
sequence, it places the result into a given
sequence - (map-into a b c) is the same as the vector
operation a b c assuming that a, b and c are
all vectors storing numbers - Assume a may store some non-numbers and we want a
pair-wise addition of a and b, here we remove all
non-numbers from a and reduce the size of b to be
equally lengthed - (map vector (remove-if-not numberp c)
(remove-if numberp d from-end t count (-
(length c) (count-if-not numberp c) 1)) - if c is (1 a b 3 c 4 5) and b is (1 2 3 4 5 6 7),
then this returns the vector (2 5 7 9) 1 1,
3 2, 4 3, 5 4 (we removed 3 letters from c
so we removed the last 3 numbers from b)
16Reduce
- Form (reduce function list) ? atom
- This applies the function to the first x
arguments in list depending on what the function
does (typically 2 arguments) and then applies the
result to the next argument, etc through the
remainder of the list - It returns the single item that the function is
supposed to return, a number for an arithmetic
operator, t/nil for a boolean function, etc - Examples
- (reduce (1 2 3 4 5)) ? 15
- (reduce (1 2 3 4 5)) ? 120
- (reduce equal (2 2 t t t) ? t
- (reduce max (3 5 4 2 1) ? 5
- And of course, we can combine functions
- (reduce (remove 0 lis)) eliminate 0s first
- (reduce (remove-if-not numberp lis))
eliminate non numbers first - (reduce max (remove 100 lis test gt))
- eliminate any numbers gt 100 first
17Other Function Functions
- flet allows you to define/declare a function
locally inside a function - labels is the same as flet except that the
functions can be recursive - macrolet is the same as flet but for macros
(which we cover next)
(defun foo (x y) (flet ((square (x) (
x x)) (cube (x) ( x x x)))
(if (gt (cube x) (square y)) (cube y) (square
x)))) (defun foo2 (x y) (flet (( (x
y) (do ((i 0 ( i 1))) (( i (length x)))
(setf (nth i x)
( (nth i x) (nth i y)))) x)) (
x y)))