Title: Building userdefined functions: the progressive envelopment technique
1Building user-defined functions the progressive
envelopment technique
- The idea define combinations of LISP primitives
through a sequence - of experiments. Thus, complex functions are build
incrementally. - Example the both-ends procedure.
- step 1 set a sample expressions that both-ends
will work on, for example (setf whole-list '(a b
c d)) - step 2 define how to get the first and last
elements - gt (first whole-list) gt A
- gt (last whole-list) gt (D)
- step 3 define how to get a list of the two
- gt (cons ... ...) the results
already obtained are enveloped in a cons form - step 4 envelop the cons form in a defun form
with whole-list as a parameter.
2Building user-defined functions the comment
translation technique
- The idea create an outline of the function in a
comment form, and - then translate comments into LISP forms.
- Example the both-ends procedure
- step 1 write a pseudo definition for both-ends
using comments instead of actual forms - (defun both-ends (whole-list)
- get the first element
- get the last element
- combine the two elements in a list)
3The comment translation technique (cont.)
- step 2 gradually translate the comments into
LISP forms - (defun both-ends (whole-list)
- (first whole-list)
- (first (last whole-list))
- combine the two elements in a list)
- step 3 complete the comment translation
- (defun both-ends (whole-list)
- (list
- (first whole-list)
- (first (last whole-list))))
4Binding parameters to initial values the LET
primitive
- The general format of the let form is the
following - (let ((ltparameter 1gt ltinitial-value 1gt)
-
- (ltparameter ngt ltinitial-value mgt))
- ltform 1gt
-
- ltform ngt)
- Example
- gt (setf whole-list (a b c d))
- gt (let ((first-el (first whole-list))
- (last-el (last whole-list)))
- (cons first-el last-el))
- (A D)
5Let can be used within defun but let parameters
are local to the let form
- Example
- gt (defun both-ends-with-let (whole-list)
- (let ((first-el (first whole-list))
- (last-el (last whole-list)))
- (cons first-el last-el)))
- BOTH-ENDS-WITH-LET
- gt (both-ends-with-let whole-list)
- (A D)
- gt (defun both-ends-with-let (whole-list)
- (let ((first-el (first whole-list))
- (last-el (last whole-list)))
- (cons first-el last-el))
- (print first-el))
- BOTH-ENDS-WITH-LET
- gt (both-ends-with-let whole-list)
- Error Unbound variable FIRST-EL in
BOTH-ENDS-WITH-LET
6Let evaluates its initial-value forms in parallel
before any let parameter is bound, while LET
does this sequentially
- Example
- gt (setf x 'first-value)
- FIRST-VALUE
- gt (let ((x 'second-value)
- (y x))
- (list x y))
- (SECOND-VALUE FIRST-VALUE)
- gt (let ((x 'second-value)
gt (let ((x
second-value)) - (y x))
or
(let ((y x)) - (list x y))
equivalent (list
x y))) - (SECOND-VALUE SECOND-VALUE)
7Predicates for establishing equality between
arguments
-
- EQUAL tests two arguments to see if their
values are the same expression. - gt (equal ( 5 5 5) 15)
- T
- gt (setf list-1 '(This is a list))
- (THIS IS A LIST)
- gt (equal '(This is a list) list-1)
- T
- gt (equal 15 15.0) 15 and
15.0 have different - NIL
internal representations - EQUALP tests two arguments to see if they are
the same expression ignoring case and data type
differences - gt (equalp 15 15.0)
- T
- gt (equalp '(THIS IS 1) '(this is 1))
- T
8Equality predicates (cont.)
- EQL tests two arguments are the same atom (i.e.
number or symbol). - gt (eql 'atom-1 'atom-1)
- T
- gt (eql 15 15)
- T
- gt (eql 15 15.0) 15 and 15.0 are not the
same atom - NIL
- tests to see if two arguments are the same
number. - gt ( 15 15.0)
- T
- EQ tests to see if two arguments are the same
symbol, i.e. if they are represented by the same
chunk of computer memory. They will be, if they
are identical symbols numbers are not always
represented by the same memory address.
9Member a predicate for testing whether the
first argument is an element of the second
argument
- Example
- gt (setf list-1 '(It is a nice day))
- (IT IS A NICE DAY)
- gt (member 'day list-1)
- (DAY)
- gt (member 'is list-1)
- (IS A NICE DAY)
- gt (member 'is '((It is) (a nice day)))
- NIL
- Note Member works only on top-level elements.
10Member tests arguments with eql
- gt (setf pairs '((a b) (c d) (e f)))
- ((A B) (C D) (E F))
- gt (member '(c d) pairs) member fails to
identify list membership of a list - NIL
-
- To modify the basic behavior of member, an
appropriate keyword - argument must be used.
- gt (member '(c d) pairs test 'equal)
- ((C D) (E F))
- gt (member '(c d) pairs test-not 'equal)
- ((A B) (C D) (E F))
- gt (member '(a b) pairs test-not 'equal)
- ((C D) (E F))
- gt (member '(c d) '((c d) (c d)) test-not
'equal) - NIL
11Keywords are self-evaluated symbols they always
start with
- gt test
- TEST keywords evaluates to themselves
- gt test symbols evaluate to the value stored
in the variable named by the - symbol
- Error Unbound variable TEST in ltfunction 1
x811040gt - Keyword arguments act as variables, storing
procedure objects. - gt (setf predicate 'equal) 'equal
produces a procedure object out of
- ltfunction 2 x8D1794gt procedure name
equal. - gt (member '(c d) pairs test predicate)
- ((C D) (E F))
12More examples
- Keyword arguments can act as variables this
makes it possible to specify any keyword
argument. - gt (member 4.0 '(6 7 8 4 9))
- NIL
- gt (setf predicate ')
- ltfunction 0 xA617E4gt
- gt (member 4.0 '(6 7 8 4 9) test predicate)
- (4 9)
- gt (setf predicate 'equalp)
equalp ignores case distinctions and - ltfunction 2 x8D1D4Cgt
data types the most liberal equality - gt (member 4.0 '(6 7 8 4 9) test predicate)
predicate - (4 9)
13Predicates for testing object types
- Atom tests to see if the argument is an atom.
- gt (atom 'pi)
- T
- gt (atom pi) pi has a
built-in value of 3.14159265358979 - T
- Numberp tests to see if the argument is a
number. - gt (numberp 'pi)
- NIL
- gt (numberp pi)
- T
- Symbolp tests to see if the argument is a
symbol. - gt (symbolp 'pi)
- T
- gt (symbolp pi)
- NIL
14Predicates for testing object types (cont.)
- Listp tests to see if the argument is a list.
- gt (listp 'pi)
- NIL
- gt (listp '(pi in a box))
- T
- Consp tests to see if the argument is a
non-empty list. - gt (consp '())
- NIL
- gt (consp nil)
- NIL
- gt (consp '(a non-empty list))
- T
- gt (consp '(a . b))
- T
15Predicates for testing for an empty list
- Null tests to see if the argument is an empty
list. - gt (null '(not an empty list))
- NIL
- gt (null 'a) the
argument can be an atom - NIL
- gt (null nil)
- T
- gt (null ())
- T
- Endp tests to see if the argument is an empty
list - gt (endp '(not an empty list))
- NIL
- gt (endp ())
- T
- gt (endp 'a) the argument must be a
list - T this is a surprising
result - the expected result is an error.
16Numerical predicates
- Numberp tests to see if the argument is a
number. - Zerop tests to see if the argument is zero.
- Plusp tests to see if the argument is a
positive number. - Minusp tests to see if the argument is a
negative number. - Evenp tests to see if the argument is an even
number. - Oddp tests to see if the argument is an odd
number. - gt tests to see if the arguments are in
descending order. - lt tests to see if the arguments are in
acsending order. - gt (plusp (- 7))
gt (gt 8 6 4 2) - NIL
T - gt (evenp ( 9 5))
gt (lt 1 3 5 7) - NIL
T - gt (oddp ( 9 5))
gt (gt 1 3 5 7) - T
NIL
17Results of two or more predicates can be combined
by means of AND, OR and NOT primitives
- And returns NIL if any of its arguments is NIL,
otherwise returns the value of the last argument - gt (setf list-1 '(a b c d))
- (A B C D)
- gt (and (member 'a list-1) (member 'c list-1))
- (C D)
- gt (and (member 'e list-1) (member 'c list-1))
- NIL
- Or returns NIL if all arguments are NIL,
otherwise retrurns the value of the first non-NIL
argument. - gt (or (member 'e list-1) (member 'c list-1))
- (C D)
- gt (or (member 'e list-1) (member 'a list-1)
(member 'c list-1)) - (A B C D)
18Logical predicates (cont.)
- Not returns T if its argument is NIL.
- gt (not nil)
- T
- gt (not ())
- T
- gt (not (member 'c '(a b d e)))
- T
- gt (not (member 'c '(a b c d)))
- NIL
- Not behaves the same way as the Null
predicate, because NIL and the empty list ( ) is
one and the same thing.
19Conditionals the if, when and unless forms
- If has the following format
- (if lttestgt ltthen formgt ltelse formgt)
- Example
- gt (setf symbol-or-number 'name)
- NAME
- gt (if (symbolp symbol-or-number) 'symbol 'number)
- SYMBOL
- gt (setf symbol-or-number '7)
- 7
- gt (if (symbolp symbol-or-number) 'symbol 'number)
- NUMBER
20Conditionals (cont.)
- When has the following format
- (when lttestgt ltthen formgt)
- This is equivalent to (if lttestgt ltthen formgt
NIL). - Unless has the following format
- (unless lttestgt ltelse formgt)
- This is equivalent to (if lttestgt NIL ltelse
formgt). - Note both when and unless may have unlimited
number of - arguments, where the first argument is always the
test, the last - argument supplies the value to be returned, and
all others are - evaluated for their side effects.
21Example
- gt (setf high 98 temperature 102)
- 102
- gt (when (gt temperature high)
- (setf high temperature)
- 'new-record)
- NEW-RECORD
- gt high
- 102
22Cond selects among alternatives
- The general form of cond is the following
- (cond (lttest 1gt ltconsequent 1-1gt ...
ltconsequent 1-ngt) - (lttest 2gt ltconsequent 2-1gt ...
ltconsequent 2-ngt) - ....
- (lttest mgt ltconsequent m-1gt ...
ltconsequent m-ngt)), - where (lttest igt ltconsequent i-1gt ...
ltconsequent i-ngt) is called a clause. - A clause whose test is evaluated to non-NIL is
said to be triggered, - and its consequents are evaluated. None of the
rest clauses is - evaluated.
23Example compute the area of object which can be
a circle or a sphere
- gt (setf object 'sphere R 1)
- 1
- gt (cond ((eq object 'circle) ( pi r r))
- ((eq object 'sphere) ( 4 pi r r)))
- 12.5663706143592
- The cond form has the following equivalent
formats - gt (cond ((eq object 'circle) ( pi r r))
- (t ( 4 pi r r)))
- 12.5663706143592
- gt (cond ((eq object 'circle) ( pi r r))
- (( 4 pi r r)))
here ( 4 pi r r) is both the test , and the - 12.5663706143592
consequent.
24More examples
- gt (setf p .6)
- 0.6
- gt (cond ((gt p .75) 'very-likely)
- ((gt p .5) 'likely)
- ((gt p .25) 'unlikely)
- (t 'very-unlikely))
- LIKELY
- gt (setf breakfast '(eggs bacon toast tea))
- (EGGS BACON TOAST TEA)
- gt (cond ((gt (length breakfast) 10) 'very-hungry)
- ((not (endp breakfast))
'just-hungry) - (t 'not-hungry))
- JUST-HUNGRY
25Write a procedure check-temperature which takes
one numerical argument, and returns VERY-HOT if
argument value is greater than 100, VERY-COLD if
it is less than 0, and NORMAL otherwise.
- gt (defun check-temperature (temp)
- (cond ((gt temp 100) 'very-hot)
- ((lt temp 0) 'very-cold)
- ('normal)))
- CHECK-TEMPERATURE
- gt (check-temperature 150)
- VERY-HOT
- gt (check-temperature 95)
- NORMAL
- gt (check-temperature (- 7))
- VERY-COLD