;;; Recall that we are trying to build a data structure ;;; called and "association" (or ASSOC) that will have ;;; a KEY and a VALUES. We will represent this data ;;; structure as a two-element list, the KEY being in ;;; the first position and the value in the second position. ;;; Finally, KEY will have data type SYMBOL and VALUES will ;;; have data type LIST of SYMBOL ;;; First attempt at a constructor function USER(1): (defun make-assoc (key values) (cons key values)) MAKE-ASSOC ;;; But the wrong thing happens when we call it: USER(2): (make-assoc 'apple '(red green)) (APPLE RED GREEN) ;;; The reason is that CONS wants as arguments ;;; a data item and a *list* of data items. ;;; We were passing it two data items: a KEY ;;; and a VALUES. ;;; Here we talked a little about ways of constructing lists. ;;; The first way is using the reader, which quotes *everything*. ;;; So in the second example, we can't evaluate the addition ;;; inside the outer list. USER(3): '(a b c) (A B C) USER(4): '(a (+ 1 2) "xyz") (A (+ 1 2) "xyz") ;;; In contrast, the LIST function evaluates *all* of its ;;; arguments. We suspend evaluation in the usual way: ;;; by quoting the argument we don't want quoted. USER(5): (list 'a (+ 1 2) "xyz") (A 3 "xyz") ;;; Two things to remember about this part of the example: ;;; (1) using asterisks in the variable name is just for ;;; documentation. *colors* is a symbol like any other. ;;; (2) using defvar just binds the symbol's value the ;;; first time. Subsequent calls to DEFVAR do NOT change ;;; its value, though you can always change its binding ;;; using SETF ;;; Here is the initial binding using DEFVAR USER(6): (defvar *colors* '((apple (red green)) (banana (yellow)) (pepper (red green yellow)) (blueberry (purple)) (cherry (red white)))) *COLORS* USER(7): *colors* ((APPLE (RED GREEN)) (BANANA (YELLOW)) (PEPPER (RED GREEN YELLOW)) (BLUEBERRY (PURPLE)) (CHERRY (RED WHITE))) ;;; But now a second DEFVAR doesn't change anything. USER(9): (defvar *colors* nil) *COLORS* USER(10): *colors* ((APPLE (RED GREEN)) (BANANA (YELLOW)) (PEPPER (RED GREEN YELLOW)) (BLUEBERRY (PURPLE)) (CHERRY (RED WHITE))) ;;; SETF does change the binding USER(11): (setf *colors* nil) NIL USER(12): *colors* NIL ;;; But DEFVAR still does not. USER(13): (defvar *colors* '((apple (red green)) (banana (yellow)) (pepper (red green yellow)) (blueberry (purple)) (cherry (red white)))) *COLORS* ;;; Now that we've looked at list constructors, ;;; we know that LIST is the right way to build ;;; our constructor for ASSOC. USER(20): (defun make-assoc (key values) (list key values)) MAKE-ASSOC USER(21): USER(21): (make-assoc 'apple '(green red)) (APPLE (GREEN RED)) ;;; Now we considered how we might have default values ;;; for one or both of the parameters. In C++ we could ;;; just define a second version of the function that ;;; had one argument, and the compiler would call the ;;; appropriate one for us. Not the case in Lisp: ;;; when we define the function a second time it just ;;; writes over the old definition. So now MAKE- ;;; ASSOC is a function of one argument. USER(22): (defun make-assoc (key) (list key '())) MAKE-ASSOC USER(23): (make-assoc 'apple '(green red)) Error: MAKE-ASSOC got 2 args, wanted at most 1. [condition type: PROGRAM-ERROR] ;;;**************************************************************** ;;; Now we talk a little about variable numbers of ;;; arguments. One way to get a function with a ;;; variable number of arguments is to put all the ;;; arguments in a list and pass it as a single argument ;;; to the function. That's how addition etc. works. ;;; This code was in error in class. Here's what the ;;; code should have looked like. What I should have ;;; done is write two functions: one with a variable ;;; number of arguments, which just calls a helper function ;;; that adds up the numbers in its single argument (which is ;;; a list. USER(9): (defun plus (&rest args) (plus-list args)) PLUS USER(16): (defun plus (&rest args) (plus-helper args)) PLUS USER(17): (defun plus-helper (list-of-args) (cond ((null list-of-args) 0) (T (+ (car list-of-args) (plus-helper (cdr list-of-args)))))) PLUS-HELPER USER(18): :untrace NIL USER(19): (plus 4 3 2 1) 10 USER(20): (plus) 0 USER(21): (trace plus plus-helper) (PLUS-HELPER PLUS) USER(22): (plus 4 3 2 1) 0: (PLUS 4 3 2 1) 1: (PLUS-HELPER (4 3 2 1)) 2: (PLUS-HELPER (3 2 1)) 3: (PLUS-HELPER (2 1)) 4: (PLUS-HELPER (1)) 5: (PLUS-HELPER NIL) 5: returned 0 4: returned 1 3: returned 3 2: returned 6 1: returned 10 0: returned 10 10 USER(23): ;;;******************************************************* ;;; Now the third way of passing parameters is "keyword" ;;; parameters. Instead of passing by position, the ;;; caller identifies each parameter by name. When ;;; the function is defined, things look just the same, ;;; except note the call for keyword parameters in the ;;; parameter list. USER(45): (defun make-assoc (&key key values) (list key values)) MAKE-ASSOC ;;; But now we don't call the function the same way: USER(46): (make-assoc 'apples '(green yellow red)) Error: APPLES not a legal keyword arg. [condition type: PROGRAM-ERROR] ;;; Instead we tag each parameter with a keyword (a symbol ;;; that starts with a colon, like :VALUES followed by a ;;; value. Now there are 2N parameters, a name and a ;;; value for each regular parameter. USER(48): (make-assoc :key 'apples :values '(green yellow red)) (APPLES (GREEN YELLOW RED)) ;;; One nice thing is that we don't have to put the parameters ;;; in order, in fact we don't have to supply all the parameters ;;; at all. ;;; Reversed parameters USER(49): (make-assoc :values '(green yellow red) :key 'apples ) (APPLES (GREEN YELLOW RED)) ;;; If you don't supply a parameter, the parameter gets bound ;;; to NIL by default. USER(50): (make-assoc) (NIL NIL) ;;; But when you define the function you can also define ;;; default values: here we define defaults for both ;;; parameters: USER(51): (defun make-assoc (&key (key 'nothing) (values '())) (list key values)) MAKE-ASSOC ;;; And now when you don't supply a parameter, the default value ;;; is used. USER(53): (make-assoc) (NOTHING NIL) ;;; But you can always override the default by supplying a ;;; a value. USER(54): (make-assoc :values '(a b c)) (NOTHING (A B C))