Transcript Lecture 5

Procedures with optional parameters which do
not require matching arguments
Example: consider the exponent function which may take one argument, m, in
which case it returns the square of m, or two arguments, m and n, in which
case it returns n power of m.
* (defun exponent (m &optional n)
optional parameter.
(if n
; if the value for an optional parameter is
(if (zerop n)
; not provided, it is assumed NIL.
1
(* m (exponent m (- n 1))))
(* m m)))
EXPONENT
* (exponent 2)
4
* (exponent 2 3)
; here n is an
Optional parameters may be given default
values, if we do not want their default value to
be NIL.
Example: the exponent procedure
* (defun exponent (m &optional (n 2))
(if (zerop n)
1
(* m (exponent m (- n 1)))))
EXPONENT
* (exponent 2)
4
* (exponent 2 3)
8
Optional parameters eliminate the need for
auxiliary procedures, because the first call uses
the default value, while all other recursive calls
ignore it
* (trace exponent)
(EXPONENT)
* (exponent 4)
| 1 Entering: EXPONENT, argument-list: (4)
|
2 Entering: EXPONENT, argument-list: (4 1)
|
| 3 Entering: EXPONENT, argument-list: (4 0)
|
| 3 Exiting: EXPONENT, value 1
|
2 Exiting: EXPONENT, value 4
| 1 Exiting: EXPONENT, value 16
16
Example
Consider math-quiz, a function which posts n math problems to the user
(who can set n as she wants) tutoring her in arithmetic (+, -, * or / is to be
set by the user, as well as the range of numbers to be exercised).
(defun math-quiz (&optional (op '+) (range 100) (n 3))
(dotimes (i n)
(exercise (random range) op (random range))))
(defun exercise (num1 op num2)
(format t "~%The expression is ")
(format t "~a ~a ~a" num1 op num2)
(format t "~% ... and the answer is ...")
(print (eval (list op num1 num2))))
* (math-quiz)
;all parameters are optional, no argument is required.
The expression is 85 + 97
... and the answer is ...
182
The expression is 25 + 76
... and the answer is ...
101
The expression is 47 + 60
... and the answer is ...
107
Example (cont.)
Optional parameters are position-dependent. To provide a non-default
value for n, we must specify values for all optional parameters that come
before n (even if they are their default values).
* (math-quiz '+ 100 5)
arguments
The expression is 61 + 46
... and the answer is ...
107
The expression is 3 + 69
... and the answer is ...
72
The expression is 34 + 76
... and the answer is ...
110
The expression is 61 + 49
... and the answer is ...
110
The expression is 3 + 78
... and the answer is ...
81
; ”+” and “100” are the default values for the first two
; they must be provided if we want to change the default value
; of the last argument.
Keyword parameters.
Keyword parameters are similar to optional parameters, except that they are
position-independent because bindings are determined by keywords not by
order.
Example: the math-quiz function.
(defun math-quiz2 (&key (op '+) (range 100) (n 3))
(dotimes (i n)
(exercise (random range) op (random range))))
(defun exercise (num1 op num2)
(format t "~%The expression is ")
(format t "~a ~a ~a" num1 op num2)
(format t "~% ... and the answer is ...")
(print (eval (list op num1 num2))))
* (math-quiz2 :n 2)
The expression is 44 + 40
... and the answer is ...
84
The expression is 84 + 43
... and the answer is ...
127
The rest parameter.
This takes as its value the list of all arguments that have been unaccounted for.
Example: consider function sum-two which takes only two arguments and adds them.
(defun sum-two (number &rest numbers)
(sum-two-aux number numbers))
(defun sum-two-aux (sum list-of-numbers)
(if (endp list-of-numbers)
sum
(sum-two-aux (+ sum (first list-of-numbers))
(rest list-of-numbers))))
* (trace sum-two sum-two-aux)
(SUM-TWO-AUX SUM-TWO)
* (sum-two 2 4 6 3 7)
| 1 Entering: SUM-TWO, argument-list: (2 4 6 3 7)
|
2 Entering: SUM-TWO-AUX, argument-list: (2 (4 6 3 7))
|
| 3 Entering: SUM-TWO-AUX, argument-list: (6 (6 3 7))
|
|
4 Entering: SUM-TWO-AUX, argument-list: (12 (3 7))
|
|
| 5 Entering: SUM-TWO-AUX, argument-list: (15 (7))
|
|
|
6 Entering: SUM-TWO-AUX, argument-list: (22 NIL)
|
|
|
6 Exiting: SUM-TWO-AUX, value 22
|
|
| 5 Exiting: SUM-TWO-AUX, value 22
.....
22
The aux parameter
This is not matched to any argument, because it is intended to define auxiliary
local variables similar to let*.
Example: the both-ends procedure.
* (setf whole-list '(a b c d))
; the value of whole-list must be provided before
(A B C D)
; function definition, otherwise an error
will occur
* (defun both-ends-new (whole-list &aux (first-el (first whole-list))
(last-list (last whole-list)))
(cons first-el last-list))
* (trace both-ends-new)
(BOTH-ENDS-NEW)
* (both-ends-new whole-list)
| 1 Entering: BOTH-ENDS-NEW, argument-list: ((A B C D))
| 1 Exiting: BOTH-ENDS-NEW, value (A D)
(A D)
Various parameters can be combined in the following order: optional
parameters come first after regular parameters, next are sole rest
parameters followed by key parameters, followed by aux parameters.
Representing structures in Lisp
We can create user-defined data types to represent any data type in Lisp.
These are called structure types, and they come with automatically created
access procedures. defstruct is the primitive that creates new structure types.
(defstruct <structure name>
(<field name 1> <default value 1>)
(<field name 2> <default value 2>)
...
(<field name n> <default value n>))
Example:
(desfstruct person
(sex nil)
(personality 'nice))
Lisp structures (cont.)
Structures are implemented as vectors, where the type (structure name) is the
zero element, field 1 is the first element, … field n is the n-th element. That is,
structures are more efficient than lists, because each element can be accessed in
a single step.
Notes: 1. ) defstruct defines a new data type.
2.) defstruct does not create instances of that data type, but automatically
creates a data-constructor procedure, data-reader procedures (a
separate one for each field), data type predicate, as well as
generalizes the setf primitive to handle the new data type.
Example:
(setf person-instance-1 (make-person)) ; make-person with no parameters creates a
;new instance of type person with fields filled with default values in defstruct
(setf person-instance-2 (make-person :sex ‘female))
;providing a new value for keyword :sex.
; default value changes by
Lisp structures (cont.)
To read data from created instances:
* (person-sex person-instance-2)
FEMALE
* (person-personality person-instance-1)
NICE
Because data is accessed by an automatically generated reader procedure,
it is said to be procedurally indexed.
To change the values of existing fields:
* (setf (person-sex person-instance-1) 'female)
* (person-sex person-instance-1)
FEMALE
Lisp structures (cont.)
defstruct also creates a data type predicate:
* (person-p person-instance-1)
T
* (person-p ‘(a b c))
NIL
Example: Define a data type rock that contains fields for color, size and
worth. Assume that the default color is gray, default size – pebble, default
worth – nothing.
(defstruct rock
(color 'gray)
(size 'pebble)
(worth 'nothing))
(setf high-hopes-rock (make-rock :color 'gold :worth 'high))
Lisp structures (cont.)
A big advantage of structure types is that one structure can include fields of
another structure, thus forming a representational hierarchy.
Example: Consider structure employee
employee
salesperson
hacker
…
* (defstruct employee
(length-of-service 0)
(payment 'salary))
EMPLOYEE
* (defstruct (hacker (:include employee))
(preferred-language 'lisp))
HACKER
* (setf employee-example (make-employee))
#S(EMPLOYEE :LENGTH-OF-SERVICE 0 :PAYMENT SALARY)
* (setf hacker-example (make-hacker))
#S(HACKER :LENGTH-OF-SERVICE 0 :PAYMENT SALARY :PREFERRED-LANGUAGE LISP)
* (employee-length-of-service employee-example)
0
* (employee-length-of-service hacker-example)
0
One structure can include another with one or more of the fields repeated in both. In such case,
the default value in more specialized structure shadows the default value in more general
structure.
* (setf employee-example (make-employee))
#S(EMPLOYEE :LENGTH-OF-SERVICE 0 :PAYMENT SALARY)
* (defstruct (salesperson (:include employee (payment 'commission)))
(preferred-car 'mercedes))
SALESPERSON
* (setf salesperson-example (make-salesperson))
#S(SALESPERSON :LENGTH-OF-SERVICE 0 :PAYMENT COMMISSION :PREFERRED-CAR MERCEDES)
* (employee-payment hacker-example)
SALARY
* (employee-payment salesperson-example)
COMMISSION
To print the contents of an instance of a structure:
* (describe hacker-example)
#S(HACKER :LENGTH-OF-SERVICE 0 :PAYMENT SALARY :PREFERRED-LANGUAGE LISP) is a named
structure of type HACKER.
It has as an included structure EMPLOYEE.
Its slot names and values are:
LENGTH-OF-SERVICE - 0
PAYMENT - SALARY
PREFERRED-LANGUAGE - LISP
To print the structure itself, however, a special printing procedure must be defined and
included in the structure’s definition.
Example:
* (defstruct (employee2 (:print-function print-employee2))
(name 'anna)
(ss# 'unknown)
(length-of-service 5)
(payment 'salary))
EMPLOYEE2
* (defun print-employee2 (structure &rest ignore)
(format t "structure for ~a with ss# ~a"
(employee2-name structure)
(employee2-ss# structure)))
PRINT-EMPLOYEE2
* (setf employee-anna (make-employee2))
structure for ANNA with ss# UNKNOWN
* (describe employee-anna)
structure for ANNA with ss# UNKNOWN is a named structure of type EMPLOYEE2.
Its slot names and values are:
NAME - ANNA
SS# - UNKNOWN
LENGTH-OF-SERVICE - 5
PAYMENT - SALARY
Representing tables as association lists: the
ASSOC and RASSOC primitives.
Consider a two-column table, where the first column contains properties of
a given object, and the second column contains the values of these
properties. Such object descriptions can be represented by expressions
called association lists (or a-lists). These have two different formats:
Format 1: ((key-1 value-1) (key-2 value-2) .... (key-n value-n))
Format 2: ((key-1 . value-1) (key-2 . value-2) .... (key-n . value-n))
The assoc primitive searches a-lists by key. Its format is the following:
(assoc <key> <a-list>)
The rassoc primitive searches a-lists by value, but it works only on a-lists of
dotted pairs. Its format is the following:
(rassoc <value> <a-list>)
Example: a table of days of the week and their
average temperatures.
* (setf week-7-1 '((Mon 28) (Tue 32) (Wed 37)
(Th 31) (Fri 33) (Sat 26) (Sun 29)))
((MON 28) (TUE 32) (WED 37) (TH 31) (FRI
33) (SAT 26) (SUN 29))
* (setf week-7-2 '((Mon . 28) (Tue . 32) (Wed .
37) (Th . 31) (Fri . 33) (Sat . 26) (Sun . 29)))
((MON . 28) (TUE . 32) (WED . 37) (TH . 31)
(FRI . 33) (SAT . 26) (SUN . 29))
* (assoc 'fri week-7-1)
(FRI 33)
* (assoc 'fri week-7-2)
(FRI . 33)
* (second (assoc 'fri week-7-1))
33
* (rest (assoc 'fri week-7-2))
33
* (rassoc 26 week-7-1)
NIL
* (rassoc 26 week-7-2)
(SAT . 26)
* (first (rassoc 26 week-7-2))
SAT
Another example on a-list of dotted pairs:
* (setf state-table '((al . alabama) (az . arizona)))
((AL . ALABAMA) (AZ . ARIZONA))
* (assoc 'al state-table)
(AL . ALABAMA)
* (rest (assoc 'al state-table))
ALABAMA
* (assoc 'ct state-table)
NIL
* (rassoc 'alabama state-table)
(AL . ALABAMA)
* (first (rassoc 'alabama state-table))
AL
Representing tables as property lists: the GET
primitives.
There are two types of values that can be assigned to symbols:
– ordinary values, such as (setf number 5), and
– property values; these are placed together in a list, called property list
(or p-list) and are “attached” to the symbol (i.e. can be accessed only
through the symbol itself).
Example: consider symbol day-1 with the following properties: avg-temp,
sun-rise, and sun-set.
* (setf (get 'day-1 'avg-temp) 37)
37
* (setf (get 'day-1 'sun-rise) '6h45m)
6H45M
* (setf (get 'day-1 'sun-set) '17h10m)
17H10M
* (get 'day-1 'sun-rise)
6H45M
;setf and get are used in combination
; to get to and set a value for a property
; of day-1
; after a value has been assigned to
; a property, get retrieves that value
The DESCRIBE and REMPROP primitives
The describe primitive can be used to see the contents of the p-list:
* (describe 'day-1)
DAY-1 is an internal symbol in package USER.
Its value is unbound.
Its function definition is unbound.
Its property list contains:
Property: SUN-SET, Value: 17H10M
Property: SUN-RISE, Value: 6H45M
Property: AVG-TEMP, Value: 37
The remprop primitive removes a property from the p-list:
* (remprop 'day-1 'avg-temp)
T
* (describe 'day-1)
DAY-1 is an internal symbol in package USER.
Its value is unbound.
Its function definition is unbound.
Its property list contains:
Property: SUN-SET, Value: 17H10M
Property: SUN-RISE, Value: 6H45M
Hash tables: yet another way to represent tables
Primitives on hash tables:
* (setf table (make-hash-table))
#<HASH-TABLE 0/37 9B:19F6>
* (setf (gethash 'al table) 'alabama)
ALABAMA
* (gethash 'al table)
ALABAMA
T
* (gethash 'ct table)
NIL
NIL
* (remhash 'al table)
T
* (gethash 'al table)
NIL
NIL
* (clrhash table)
#<HASH-TABLE 0/37 9B:19F6>
; initializes the table
; modifies the table
; retrieves values
; removes a key-value pair from the table
; removes all key-value pairs from the table
Data abstraction makes it easy to manipulate lists
Consider student database, where each student is represented by the following list:
* (setf student1 '((Paul Bennett) ((hw1 4.3) (hw2 5.0) (hw3 3.5) (hw4 4.8)
(hw5 4.9)) ((test1 9.5) (test2 8.7)) (classw 10.0) (project 18) (final 28)))
Given this representation, to find HW2 grade of student1:
* (second (second (second student1)))
==> 5.0
To add student major as a second element of student list:
* (setf student1 (append (list (first student1)) (list '(CIT major)) (rest student1)))
((PAUL BENNETT) (CIT MAJOR) ((HW1 4.3) (HW2 5.0) (HW3 3.5) (HW4 4.8)
(HW5 4.9)) ((TEST1 9.5) (TEST2 8.7)) (CLASSW 10.0) (PROJECT 18) (FINAL 28))
Now to get to student1 HW2 grade, a different reader procedure must be used:
* (second (second (third student1)))
That is, any change in data representation requires a change in the “reader” procedure.
Furthermore, all of the details of the data representation must be remembered in order to
construct the appropriate “reader”.
Better representation for student database example.
Consider the following association lists for representing students:
((name (Paul Bennett))
(major CIT)
(homeworks (4.3 5.0 3.5 4.8 4.9))
(tests (9.5 8.7))
(claswork 10.0)
(project 18)
(final 28))
((name (Abe Cadman))
(major CIT)
(status withdrawn))
The following procedure will construct each of these a-lists:
(defun construct-student (name major hw1 &optional hw2 hw3 hw4 hw5 test1 test2 classwork project final)
(if (eql hw1 'withdrawn) (list (list 'name name) (list 'major major) (list 'status 'withdrawn))
(list (list 'name name) (list 'major major)
(list 'homeworks (list hw1 hw2 hw3 hw4 hw5))
(list 'tests (list test1 test2)) (list 'classwork classwork)
(list 'project project) (list 'final final))))
To construct student1 and student2, we say:
* (setf student1 (construct-student '(Paul Bennett) 'CS 4.3 5.0 3.5 4.8 4.9 9.5 8.7 10.0 18 28))
((NAME (PAUL BENNETT)) (MAJOR CS) (HOMEWORKS (4.3 5.0 3.5 4.8 4.9)) (TESTS (9.5 8.7))
(CLASSWORK 10.0) (PROJECT 18) (FINAL 28))
* (setf student2 (construct-student '(Abe Cadman) 'CS 'withdrawn))
((NAME (ABE CADMAN)) (MAJOR CS) (STATUS WITHDRAWN))
Example (cont.)
I can now create “reader” procedures to access each one of the elements of
student1. For example, to access the grade for HW2:
(defun hw2 (student1)
(if (eql (second (assoc 'status student1)) 'withdrawn)
(print 'withdwrawn)
(second (second (third student1)))))
Once constructor and reader procedures are defined, the programmer
can forget about the details of the representation. If the latter changes,
only the affected procedures must be re-written.
To build the student database, I must say:
(setf students (list
(construct-student '(Paul Bennett) 'CS 4.3 5.0 3.5 4.8 4.9 9.5 8.7 10.0 18 28)
(construct-student '(Abe Cadman) 'CS 'w)
(construct-student '(Nelson DaCunha) 'CS 4.8 4.0 4.5 3.8 5.0 8.5 9.7 10.0 17 25)))
List transformation: returns a list containing only
particular elements of the original list
Example: Transform the students list into a list containing only student names.
(defun names (students)
(if (endp students)
nil
(cons (get-name (first students))
(names (rest students)))))
(defun get-name (student)
(second (first student)))
* students
(((NAME (PAUL BENNETT)) (MAJOR CS) (HOMEWORKS (4.3 5.0 3.5 4.8 4.9))
(TESTS (9.5 8.7)) (CLASSWORK 10.0) (PROJECT 18) (FINAL 28)) ((NAME
(ABE CADMAN)) (MAJOR CS) (HOMEWORKS (W NIL NIL NIL NIL)) (TESTS (NIL
NIL)) (CLASSWORK NIL) (PROJECT NIL) (FINAL NIL)) ((NAME (NELSON
DACUNHA)) (MAJOR CS) (HOMEWORKS (4.8 4.0 4.5 3.8 5.0)) (TESTS (8.5
9.7)) (CLASSWORK 10.0) (PROJECT 17) (FINAL 25)))
* (names students)
((PAUL BENNETT) (ABE CADMAN) (NELSON DACUNHA))
List transformation procedures: a general
format
When transforming a list into another list, the resulting list is of the same
length as the original list. The general format of the transformation procedure
is the following:
(defun <transformation-proc> (list-1)
(if (endp list-1)
NIL
(cons (<get-desired-element-proc> (first list-1))
(<transformation-proc> (rest list-1)))))
The MAPCAR primitive transform lists
Mapcar has the following format:
(mapcar #’ <procedure object> <list-1> ...<list-n> ), where:
– <procedure object> supplies the name of the transforming procedure,
– <list-1>,..., <list-n> supply lists of elements to be transformed.
Examples:
* (mapcar #'zerop '(8 5 0 1 0 5))
(NIL NIL T NIL T NIL)
* (mapcar #'= '(1 2 3 4 5) '(1 3 5 4 8))
(T NIL NIL T NIL)
* (mapcar #'get-name students)
((PAUL BENNETT) (ABE CADMAN) (NELSON DACUNHA))
* (mapcar #'get-hw2 students)
(5.0 NIL 4.0)
Filtering undesired elements
Consider the list (5.0 NIL 4.0). If we want to compute the avarage, we
must first filter NILs.
(defun clean-grade-list (grade-list)
(cond ((endp grade-list) nil)
((numberp (first grade-list))
(cons (first grade-list) (clean-grade-list (rest
grade-list))))
(t (clean-grade-list (rest grade-list)))))
* (clean-grade-list (mapcar #'get-hw2 students))
(5.0 4.0)
Filtering procedures: a general format
Procedures for filtering out elements that do not satisfy the desired property is
the following:
(defun <filtering-procedure> (list-1)
(cond ((endp <list-1>) nil)
((<testing-for-desired-property-proc> (first list-1))
(cons (first list-1) (filtering-procedure> (rest list-1))))
(t (filtering-procedure> (rest list-1))))
The resulting list may contain the same or a smaller number of elements
compared to the original list.
The REMOVE-IF and REMOVE-IF-NOT
primitives simplify filtering procedures

Remove-if removes all elements of list, which satisfy the predicate serving
as a filter. Its general format is the following:
(remove-if #’<procedure object> <list>)

Remove-if-not removes all elements of list, which do not satisfy the
predicate serving as a filter. Its general format is the following:
(remove-if-not #’<procedure object> <list>)
where:
– <procedure object> supplies the name of the filtering procedure,
– <list> is the list of elements to be filtered.
Examples:
Filter NILs from the grades list:
* (remove-if #'null (mapcar #'get-hw2 students))
(5.0 4.0)
Filter non-NILs from the grades list:
* (remove-if-not #'null (mapcar #'get-hw2 students))
(NIL)
Filter zeros from a given list of numbers:
* (remove-if #'zerop '(2 0 4 6 0 0))
(2 4 6)
Filter non-even elements of a given list of numbers:
* (remove-if-not #'evenp '(3 4 5 6 7 8))
(4 6 8)
Mapping primitives can take as a procedure
object an already defined function or a lambda
expression
Lambda expressions are “anonymous” functions.
Example: compute the square of m
* #'(lambda (n) (* n n))
#<LISP::SCANNED (LAMBDA (N) (DECLARE) (*
* (setf square #'(lambda (n) (* n n)))
#<LISP::SCANNED (LAMBDA (N) (DECLARE) (*
* square
#<LISP::SCANNED (LAMBDA (N) (DECLARE) (*
* (mapcar square '(1 2 3 4 5))
(1 4 9 16 25)
* (mapcar #'(lambda (n) (* n n)) '(1 2 3
(1 4 9 16 25)
N N))>
N N))>
N N))>
4 5))
Lambda expressions make it possible to create new functions in
run time. Such
run-time functions are called closures.
Counting list elements that satisfy a desired
property
Consider the student DB example, and assume that we want to count students
that have withdrawn from the class. The following function will do the job:
(defun count-w (student-list)
(cond ((endp student-list) 0)
((eql (second (first student-list)) 'withdrawn)
(+ 1 (count-w (rest student-list))))
(t (count-w (rest student-list)))))
The general format of any counting procedure is:
(defun <counting proc> (list-1)
(cond ((endp list-1) 0)
((<testing-desired-prop proc> (first list-1))
(+ 1 (<counting proc> (rest list-1))))
(t (<counting proc> (rest list-1)))))
The COUNT-IF and COUNT-IF-NOT primitives
To count the number of students that have withdrawn from the class,
we can say:
* (count-if #'(lambda (student) (eql (second student) 'w))
student-list)
2
Or, if we have defined predicate get-w, such that
(defun get-w (student) (eql (second student) 'w))
the equivalent query is:
* (count-if #'get-w student-list)
2
To count the number of students that have not withdrawn from the
class, we can say:
* (count-if-not #'get-w student-list)
3
COUNT-IF counts the number of elements on a list that
satisfy a given property; EVERY / SOME test if every /
some element on the list satisfies a given property
Examples:
* (count-if #'oddp '(1 2 3 4 5))
3
* (every #'oddp '(1 2 3 4 5))
NIL
* (some #'oddp '(1 2 3 4 5))
T
Searching for an element that satisfies a desired
property
Assume we want to search for a student who have 20 points on the
project. The following functions will do the job:
(defun search-project-20 (student-list)
(cond ((endp student-list) nil)
((eql 20 (get-project (first student-list)))
(first (first student-list)))
(t (search-project-20 (rest student-list)))))
(defun get-project (student)
(second (fifth student)))
* (search-project-20 student-list)
(SUSAN MELVILLE)
General format of a searching procedure and its
substitute primitives FIND-IF and FIND-IF-NOT
(defun <searching procedure> (list-1)
(cond ((endp list-1) nil)
((<testing-desired-prop proc> (first list-1))
(first list-1))
(t (<searching procedure> (rest list-1)))))
Find-if and find-if-not are primitives that search for the first element of a list
satisfying (not satisfying) a desired property. In the student DB example, to ask
if at least one student has (has not) 20 points on the project, we can say:
* (first (find-if #'(lambda (student) (eql 20 (get-project
student))) student-list))
(SUSAN MELVILLE)
* (first (find-if-not #'(lambda (student) (eql 20 (get-project
student))) student-list))
(PAUL BENNETT)
The FUNCALL and APPLY primitives allow
procedures to be passed as arguments
The funcall primitive has the following format:
(funcall #’<procedure object> <argument-1> ... <argument-n>)
 The apply primitive has the following format:
(apply #’<procedure object> (<argument-1> ... <argument-n>))
Examples:

* (defun pass-operator
(funcall operator
PASS-OPERATOR
* (pass-operator 33 22
55
* (pass-operator 33 22
11
* (pass-operator 33 22
726
(operand-1 operand-2 operator)
operand-1 operand-2))
'+)
'-)
'*)
Examples (cont.)
Notice that without funcall, the pass-operator function will not work:
* (defun pass-operator (operand-1 operand-2 operator)
(operator operand-1 operand-2))
PASS-OPERATOR
* (pass-operator 33 22 '+)
*** Debugger warning: leftover specials ***
>>> Error: {Determining function in error.}
>>> Error:Undefined function: OPERATOR
while evaluating: (OPERATOR OPERAND-1 OPERAND-2)
The apply primitive can be used instead of funcall as follows:
* (defun pass-operator (operand-1 operand-2 operator)
(apply operator (list operand-1 operand-2)))
PASS-OPERATOR
* (pass-operator 33 22 '+)
55
* (pass-operator 33 22 '-)
11
More examples
* (funcall #'first '(a b c d))
A
* (funcall #'list 'a 'b 'c 'd)
(A B C D)
* (funcall #'append '(a b) '(c d))
(A B C D)
* (apply #'first '((a b c d)))
A
* (apply #'list '(a b c d))
(A B C D)
* (apply #'append '((a b) (c d)))
(A B C D)
* (funcall #'+ 1 2 3 '(4 5 6))
*** Debugger warning: leftover
specials ***
>>> Error: {Determining function in
error.}
>>> Error:+: wrong type argument: (4
5 6)
A NUMBER was expected.
In some cases, apply may have more
than two arguments:
* (apply #'+ 1 2 3 '(4 5 6))
21