Lecture 3

advertisement
Building user-defined functions: the progressive
envelopment technique
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
* (first whole-list)
* (last whole-list)
==> A
==> (D)
– step 3: define how to get a list of the two
* (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.
Building user-defined functions: the comment
translation technique
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)
The 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))))
Binding parameters to initial values: the LET
primitive
The general format of the let form is the following:
(let ((<parameter 1> <initial-value 1>)
…
(<parameter n> <initial-value m>))
<form 1>
…
<form n>)
Example:
* (setf whole-list ’(a b c d))
* (let ((first-el (first whole-list))
(last-el (last whole-list)))
(cons first-el last-el))
(A D)
Let can be used within defun but let parameters
are local to the let form
Example:
* (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
* (both-ends-with-let whole-list)
(A D)
* (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
* (both-ends-with-let whole-list)
;; Error: Unbound variable FIRST-EL in BOTHENDS-WITH-LET
Let can be used within defun but let parameters
are local to the let form: another example
CG-USER(3): (defun another-example ()
(let ((a 5)
(b 6)
(c 7))
(print (+ a b c)))
(print (+ a b c)))
ANOTHER-EXAMPLE
CG-USER(4): (another-example)
18
Error: Attempt to take the value of the
unbound variable `A'.
[condition type: UNBOUND-VARIABLE]
LET evaluates its initial-value forms in parallel before
any LET parameter is bound, while LET* does this
sequentially.
Example:
* (setf x 'first-value)
FIRST-VALUE
* (let ((x 'second-value)
(y x))
(list x y))
(SECOND-VALUE FIRST-VALUE)
* (let* ((x 'second-value)
(y x))
(list x y))
(SECOND-VALUE SECOND-VALUE)
or
equivalent
* (let ((x ‘second-value))
(let ((y x))
(list x y)))
Predicates for establishing equality between
arguments
EQUAL : tests two arguments to see if their values are the same.
* (equal (+ 5 5 5) 15)
T
* (setf list-1 '(This is a list))
(THIS IS A LIST)
* (equal '(This is a list) list-1)
T
* (equal 15 15.0)
; 15 and 15.0 have different
NIL
; internal representations
EQUALP: tests two arguments to see if they are the same ignoring case and data type
differences
* (equalp 15 15.0)
T
* (equalp '(THIS IS 1) '(this is 1))
T
Equality predicates (cont.)
EQL : tests two arguments to see if they are are the same atom (i.e. number or
symbol).
* (eql 'atom-1 'atom-1)
T
* (eql 15 15)
T
* (eql 15 15.0) ; 15 and 15.0 are not the same atom
NIL
= : tests to see if two arguments are the same number.
* (= 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.
Member : a predicate for testing whether the
first argument is an element of the second
argument
Example:
* (setf list-1 '(It is a nice day))
(IT IS A NICE DAY)
* (member 'day list-1)
(DAY)
* (member 'is list-1)
(IS A NICE DAY)
* (member 'is '((It is) (a nice day)))
NIL
Member works only on top-level elements.
Member tests arguments with eql
* (setf pairs '((a b) (c d) (e f)))
((A B) (C D) (E F))
* (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.
* (member '(c d) pairs :test #'equal)
((C D) (E F))
* (member '(c d) pairs :test-not #'equal)
((A B) (C D) (E F))
* (member '(a b) pairs :test-not #'equal)
((C D) (E F))
* (member '(c d) '((c d) (c d)) :test-not #'equal)
NIL
Keywords are self-evaluated symbols; they
always start with :
* :test
:TEST ; keywords evaluates to themselves
* test ; symbols evaluate to the value stored in the variable named by the
; symbol
;; Error: Unbound variable TEST in #<function 1 #x811040>
Keyword arguments act as variables, storing procedure objects.
* (setf predicate #'equal)
; #'equal produces a procedure object out of
#<function 2 #x8D1794>
; procedure name equal.
* (member '(c d) pairs :test predicate)
((C D) (E F))
More examples
* (member 4.0 '(6 7 8 4 9))
NIL
* (setf predicate #'=)
#<function 0 #xA617E4>
* (member 4.0 '(6 7 8 4 9) :test predicate)
(4 9)
* (setf predicate #'equalp)
#<function 2 #x8D1D4C>
* (member 4.0 '(6 7 8 4 9) :test predicate)
(4 9)
; equalp ignores case differences and
; data types. It is the most liberal equality
; predicate
Predicates for testing object types
Atom : tests to see if the argument is an atom.
* (atom 'pi)
T
* (atom pi)
T
; pi has a built-in value of 3.14159265358979
Numberp : tests to see if the argument is a number.
* (numberp 'pi)
NIL
* (numberp pi)
T
Symbolp: tests to see if the argument is a symbol.
* (symbolp 'pi)
T
* (symbolp pi)
NIL
Predicates for testing object types (cont.)
Listp : tests to see if the argument is a list.
* (listp 'pi)
NIL
* (listp '(pi in a box))
T
Consp : tests to see if the argument is a non-empty list.
* (consp '())
NIL
* (consp nil)
NIL
* (consp '(a non-empty list))
T
* (consp '(a . b))
T
Predicates for testing for an empty list
Null : tests to see if the argument is an empty list.
* (null '(not an empty list))
NIL
* (null 'a)
; the argument can be an atom
NIL
* (null nil)
T
* (null ())
T
Endp : tests to see if the argument is an empty list
* (endp '(not an empty list))
NIL
* (endp ())
T
* (endp 'a)
; the argument must be a list
T
; this is a surprising result - the expected result is an error.
Numerical 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.
> : tests to see if the arguments are in descending order.
< : tests to see if the arguments are in ascending order.
* (plusp (- 7))
NIL
* (evenp (* 9 5))
NIL
* (oddp (* 9 5))
T
* (> 8 6 4 2)
T
* (< 1 3 5 7)
T
* (> 1 3 5 7)
NIL
Results 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
* (setf list-1 '(a b c d))
(A B C D)
* (and (member 'a list-1) (member 'c list-1))
(C D)
* (and (member 'e list-1) (member 'c list-1))
NIL
•
Or : returns NIL if all arguments are NIL, otherwise returns the value of the first
non-NIL argument.
* (or (member 'e list-1) (member 'c list-1))
(C D)
* (or (member 'e list-1) (member 'a list-1) (member 'c list-1))
(A B C D)
Logical predicates (cont.)
•
Not : returns T if its argument is NIL.
* (not nil)
T
* (not ())
T
* (not (member 'c '(a b d e)))
T
* (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.
Conditionals : if, when and unless forms
• If has the following format:
(if <test> <then form> <else form>)
Example:
* (setf symbol-or-number 'name)
NAME
* (if (symbolp symbol-or-number) 'symbol 'number)
SYMBOL
* (setf symbol-or-number '7)
7
* (if (symbolp symbol-or-number) 'symbol 'number)
NUMBER
Conditionals (cont.)
• when has the following format:
(when <test> <then form>)
This is equivalent to (if <test> <then form> NIL).
• unless has the following format:
(unless <test> <else form>)
This is equivalent to (if <test> NIL <else form>).
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.
Example
* (setf high 98 temperature 102)
102
* (when (> temperature high)
(setf high temperature)
'new-record)
NEW-RECORD
* high
102
Cond selects among alternatives
The general form of cond is the following:
(cond (<test 1> <consequent 1-1> ... <consequent 1-n>)
(<test 2> <consequent 2-1> ... <consequent 2-n>)
....
(<test m> <consequent m-1> ... <consequent m-n>)),
where (<test i> <consequent i-1> ... <consequent i-n>) 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.
Example: compute the area of object which can be a
circle or a sphere
* (setf object 'sphere R 1)
1
* (cond ((eq object 'circle) (* pi r r))
((eq object 'sphere) (* 4 pi r r)))
12.5663706143592
The cond form has the following equivalent formats:
* (cond ((eq object 'circle) (* pi r r))
(t (* 4 pi r r)))
12.5663706143592
* (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.
More examples
* (setf p .6)
0.6
* (cond ((> p .75) 'very-likely)
((> p .5) 'likely)
((> p .25) 'unlikely)
(t 'very-unlikely))
LIKELY
* (setf breakfast '(eggs bacon toast tea))
(EGGS BACON TOAST TEA)
* (cond ((> (length breakfast) 10) 'very-hungry)
((not (endp breakfast)) 'just-hungry)
(t 'not-hungry))
JUST-HUNGRY
Example
Write 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.
* (defun check-temperature (temp)
(cond ((> temp 100) 'very-hot)
((< temp 0) 'very-cold)
('normal)))
CHECK-TEMPERATURE
* (check-temperature 150)
VERY-HOT
* (check-temperature 95)
NORMAL
* (check-temperature (- 7))
VERY-COLD
Download