CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 2
Example: Our-third
Function to get the third element of a list> (third '(a b c d e))c
How can we do this without using THIRD?– Want to create our own function, OUR-THIRD that mimics THIRD
> (our-third '(a b c d e))c
– Definition of OUR-THIRD> (defun our-third (x) (first (rest (rest x))))OUR-THIRD
Many Lisp built-in functions can be written using other Lisp built-in functions See Appendix B, Lisp in Lisp.
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 3
Example: Both-ends
Define a procedure that gives both ends of a list> (setf itinerary ’(Albany NYC Chicago Seattle Anchorage))> (both-ends itinerary)
(ALBANY ANCHORAGE)
Three steps Get first element
> (first itinerary)ALBANY
Get last element> (first (last itinerary))
ANCHORAGE
Combine the two> (list (first itinerary) (first (last itinerary)))
Define procedure> (defun both-ends (l) (list (first l) (first (last l))))
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 4
Example: Distance
Write a function to find the distance between 2 points.
> (defun sqr (x) (* x x))SQR
> (defun distance (x1 y1 x2 y2) (sqrt (+ (sqr (- x2 x1)) (sqr (- y2 y1)))))
DISTANCE> (setf originx 0 originy 0)
0> (distance originx originy 2 2)
1.414– Must have correct number of arguments– The value of each argument is copied to the corresponding parameter
originx copied to x1originy copied to y12 copied to x22 copied to y2
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 5
Scope Consider
> (setf a ’ORIG-A b ’ORIG-B c ’ORIG-C)ORIG-C
> (list a b c)(ORIG-A ORIG-B ORIG-C)
> (defun myfun (a) (setf a ’myfun-a) (setf b ’myfun-b) (list a b))
> (myfun c)(MYFUN-A MYFUN-B)
> (list a b c)(ORIG-A MYFUN-B ORIG-C)
Value of C is copied to A– Parameter passing: Pass by Value (Like C/C++ default)
Global variables are still accessible!– Like C/C++ Global variables
Lexical variable: variable declared as a parameter Special variable: variable not declared as a parameter
A,B,C A
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 6
Let
Let = Lisp’s way of defining local variables(let ( (<var1> <value1>) (<var2> <value2>) … ) <expr1> <expr2>)
Example(defun distance (x1 y1 x2 y2) (let ( (dx (- x2 x1)) (dy (- y2 y1)) ) (sqrt (+ (sqr dx) (sqr dy))) ))
Let evaluates in parallel (not sequentially) Uses original values of variables in all (<var> <value>) pairs
(let ( (dx (- x2 x1)) (dy (- y2 y1)) (dx_sqr (sqr dx)) (dy_sqr (sqr dy)) ) ;;
Won’t work! (sqrt (+ dx_sqr dy_sqr)) )
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 7
Let vs. Let*
Let - Parallel evaluation> (setf x ’outside)
OUTSIDE> (let ((x ’inside) (y x)) (list x y))
(INSIDE OUTSIDE)
Let* - Sequential evaluation> (setf x ’outside)
OUTSIDE> (let* ((x ’inside) (y x)) (list x y))
(INSIDE INSIDE)
Let* Implementation of distance(let* ( (dx (- x2 x1)) (dy (- y2 y1)) (dx_sqr (sqr dx)) (dy_sqr (sqr dy)) ) ;;
OK! (sqrt (+ dx_sqr dy_sqr)) )
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 8
Exercise
1. Rewrite the distance function to use “setf” instead of “let”. Explain the difference.
2. Write the function solve-quadratic such that, given a,b,c, the function finds all values of x for which ax^2 + bx + c = 0.
x1 = (-b + sqrt (b^2 - 4ac)) / 2ax2 = (-b - sqrt (b^2 - 4ac)) / 2a
The function should return both values of x as a list of two elements. For example,
> (solve-quadratic 1 -2 1)(1.0 1.0)
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 9
Example: Solve-quadratic
Solution to solve-quadratic(defun solve-quadratic (a b c) (let ((sqrt_clause (- (* b b) (* 4 a c))) (neg_b (- b)) (two_a (* 2 a)) ) (list (/ (+ neg_b (sqrt sqrt_clause)) two_a) (/ (- neg_b (sqrt sqrt_clause)) two_a))))
Notes - No error checking is done (- (* b b) (* 4 a c)) might result in a negative number
– What happens when you try to take sqrt of a negative number?
What happens when the value of A is zero?
Need Predicates - to determine when error conditions occur (e.g. =) Conditionals - to do something else when error occurs
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 10
EQUAL does not = EQL
Several predicates check equality Slightly different semantics
– Can be confusing
Equality predicates Equal - Are two argument values the same expression? Eql - Are two argument values the same symbol or number? Eq - Are two argument values the same symbol? = - Are two argument values the same number?
General Rule of Thumb Use = when you want to compare numbers Use EQUAL otherwise
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 11
Relationships Bet Equality Pred’s
EQ vs. = = returns T if two numbers are the same, regardless of their type EQ returns T if two arguments are the same symbol
– Same chunk of memory– Identical primitives sometimes are EQ
Examples> (eq 4 4) ;; EQ:T =:T (C)> (eq (/ 1 4) 0.25) ;; EQ:NIL =:T (A)> (eq ’A ’A) ;; EQ:T =:Error (D)> (eq 4.0 4.0);; EQ:NIL =:T (A)
= EQ EQL EQUAL
A
B C
D
F
E
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 12
EQL vs. =
EQL Arguments are EQL if they are either EQ or they are equivalent
numbers of the same type Examples
> (eq 4.0 4.0) ;; EQ:NIL EQL:T =:T (B)> (eq 4 4.0) ;; EQ:NIL EQL:NIL =:T (A)
EQUAL Arguments are EQUAL if they are either EQL or they are Lists
whose elements are EQUAL Performs an element by element comparison of lists
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 13
EQUAL vs. EQL vs. =
EQUAL vs. EQL> (setf X ’(A (B (C))))> (setf Y X)> (setf Z ’(A (B (C))))
X and Y are EQUAL and EQL X and Z are EQUAL but not EQL
– They don’t point to the SAME memory– But, if you do an element by element
comparison of the lists, they are equal
EQUAL vs. => (EQUAL 4 4.0)
NIL> (= 4 4.0)
T
B
C
A
Z
B
C
A
X
Y
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 14
Data Type Predicates
Data Type Predicates Compare objects to determine their type
– atom - Is argument an atom?– numberp - Is argument a number?– symbolp - Is argument a symbol (a non-numeric atom)?– listp - Is argument a list?
Examples> (list pi ’ABC)
(3.14159 ABC)> (list (atom pi) (atom ’ABC))
(T T)> (list (numberp pi) (numberp ’ABC))
(T NIL)> (list (symbolp pi) (symbolp ’ABC))
(NIL T)> (list (listp pi) (listp ’ABC) (listp ’(A B))
(NIL NIL T)
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 15
Number Predicates
Number predicates numberp - Is it a number? zerop - Is it zero? Argument must be a number plusp - Is it positive? Argument must be a number minusp - Is it negative? Argument must be a number evenp - Is it even? oddp - Is it odd? > - Is the list of arguments in descending order? (e.g. (> 5 4)) < - Is the list of arguments in ascending order?
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 16
If, When, Unless
If (if <test> <then-form> <else-form>) if <test> is true
– <then-form> is evaluated, – otherwise <else-form> is evaluated.
When (when <test> <then-form1> <then-form2> …) If <test> is true, then other arguments are evaluated.
Unless (unless <test> <else-form1> <else-form2> …) If <test> is false, then other arguments are evaluated.
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 17
Cond
Cond format(COND (<test1> <then-1-1> <then-1-2> …) (<test2> <then-2-1> <then-2-2> …) (<test3> <then-3-1> <then-3-2> …) … (<testi> <then-i-1> <then-i-2> …) … (<testm> <then-m-1> <then-m-2> …)
Semantics 1. Evaluate <test1>, <test2>, … until you get to a <testi> such
that <testi> evaluates to a nonNIL value 2. Evaluate <then-i-1> <then-i-2> … 3. Return value of the last <then-i-n> clause executed
If all tests evaluate to NIL, result of COND is NIL
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 18
Example
Define a function to find area(defun area (type r) (cond ((equal type ’circle) (* PI R R)) ((equal type ’sphere) (* 4 PI R R)) ((equal type ’square) (* R R) ((equal type ’cube) (* 6 R R)) ( t (print “Error”) ))
Define a function, compute-grade, to determine the letter grade.
(compute-grade 81.5)B
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 19
Case
Similar to COND, but is more elegant when checking for equality
(defun area (type r) (case type (circle (* PI R R)) (sphere (* 4 PI R R)) (square (* R R) (cube (* 6 R R)) (otherwise (print “Error”) ))
Format(case <key-form> (<key 1> <then-1-1> <then-1-2> …) (<key 2> <then-2-1> <then-2-2> …) … (<key m> <then-m-1> <then-m-2> …) (otherwise <then-o-1> <then-o-2> …)
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 20
Case (2)
Notes <key-form> is evaluated
– But, <key 1> <key 2>, … are NOT evaluated
“OTHERWISE” or “T” always evaluate to nonNIL If no matches are found, the CASE statement returns NIL A <key> form may contain a list. In this case, the <key-form>
can match ANY element of the list(defun circularp (type) (case type ((circle sphere) ’T) (otherwise NIL)))(circularp ’cube)
– Technical note: the MEMBER function is used to do the comparison.
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 21
Both-ends Revisited
Both-ends with error handling(defun both-ends (l) (if (listp l) (case (length l) (0 NIL) (1 (list (first l) (first l))) (t (list (first l) (first (last l))))) NIL))
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 22
Solve-quadratic revisited
Solve-quadratic with error checking Checks for errors; returns NIL instead
(defun solve-quadratic (a b c) (if (not (and (numberp a) (numberp b) (numberp c))) NIL ; at least one argument is not a number (let ((sqrt_clause (- (* b b) (* 4 a c))) (neg_b (- b)) (two_a (* 2 a)) ) (cond ((minusp sqrt_clause) NIL) ((= 0 a) NIL) ; Could have used zerop (t (list (/ (+ neg_b (sqrt sqrt_clause)) two_a) (/ (- neg_b (sqrt sqrt_clause)) two_a)))))))
What happens if we don't include the clause(minusp sqrt_clause) …
– By default, Lisp arithmetic operations handle complex numbers
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 23
Factorial Definition of Factorial C++ Implementation
int factorial (int x) { int i,f; f = 1; for (i=1; i<=x; i++) f = f * i; return f;}
Lisp Implementation(defun factorial (n) (let ((f 1)) (dotimes (i n f) (setf f (* f (+ i 1))))))
– Note: To more easily compare, C++ loop could have been written as:for (i=0; i<x; i++) f = f * (i + 1);
Tip: Compute factorial(100) using C/C++ and Lisp
)1(
*...*3*2*1
1
0
1
i
Ni
N
i
N
i
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 24
DoTimes
DOTIMES is Lisp’s way of doing iteration C/C++
for (i=0; i<n; i++) { <body>}
Lisp (dotimes (i n) <body>)
First parameter is a list with three elements– counter variable (e.g. i)– number of times to iterate (e.g. n)
Counter variable (i) ranges from 0 to n-1 Optional return value can be specified (default is NIL)
(dotimes (i n return_value) <body>)
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 25
Recursively Calculating Factorial Mathematical Definition of Factorial
C/C++ Implementationlong Factorial (long X) { if (X <= 1)
return 1; else
return X * Factorial(X-1);}
Lisp Implementation(defun recursive-factorial (x) (if (<= x 1) 1 (* x (recursive-factorial (- x 1)))))
XX
X X X!
, ;
*( )!,
1 1
1 1
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 26
Recursive calls Show recursion when calling (recursive-factorial 4)
Begin (recursive-factorial 4) Since 4>1, evaluate 4 * (recursive-factorial 3) Begin (recursive-factorial 3) Since 3>1, evaluate 3 * (recursive-
factorial 2) Begin (recursive-factorial 2) Since 2>1, evaluate 2*(recursive-
factorial 1) Begin (recursive-factorial 1) Since 1<=1, return 1 End (recursive-factorial 1),
returns 1 2 * (recursive-factorial 1) = 2 * 1
= 2 End (recursive-factorial 2), returns 2 3 * (recursive-factorial 2) = 3 * 2 = 6 End (recursive-factorial 3), returns 6 4 * (recursive-factorial 3) = 4 * 6 = 24End (recursive-factorial 4), returns 24
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 27
MEMBER
MEMBER Checks if first argument is an element of second argument Returns what is left of the list when symbol is found
> (setf sentence ’(tell me more about your mother please))
(TELL ME MORE ABOUT YOUR MOTHER PLEASE)> (member ’mother sentence)
(MOTHER PLEASE)
Only checks the top level for a match (using EQL)> (member ’mother ’((father son) (mother
daughter)))NIL
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 28
Example: Our-member
Our-Member(defun our-member (obj aList) (if (null aList) nil (if (eql (first aList) obj) aList (our-member obj (rest aList)))))
This "design pattern" is used frequently in Lisp for recursive methods that operate on Lists
(defun <recursive-function (aList) (if <base condition> <terminate with return value> <process first element> <recursively process everything except the first element>))
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 29
MEMBER; Keyword Modifiers
MEMBER tests for membership using EQL> (setf pairs ’((maple shade) (apple fruit)))> (first pairs)
(MAPLE SHADE)> (member (first pairs) pairs)
((MAPLE SHADE) (APPLE FRUIT))> (member ’(maple shade) pairs)
NIL
Use keyword modifier to override> (member ’(maple shade) pairs :test #’equal)
((MAPLE SHADE) (APPLE FRUIT))
:test is a keyword; it expects an argument of type procedure #’equal is a keyword argument
– #’ is a macro– #’equal expands to (function equal)– Returns the procedure object named “equal”
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 30
Keywords; Procedure Macro
Why use keywords? Why not just an optional parameter?
> (member ’(maple shade) pairs #’equal)
Keywords allow more than one type of behavior modification– :test-not is another keyword for MEMBER
Why use procedure objects? Why not just allow procedure name to be passed?
> (member ’(maple shade) pairs :test equal)
Procedure objects can be bound to variable names> (setf predicate #’equal)> (member ’(maple shade) pairs :test predicate)
Can pass them as parameters to functions, etc.
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 31
Recursive Definition of Length
Length(defun mylength (l) (if (endp l) 0 (+ 1 (mylength (rest l)))))
Alternate definition(defun mylength2 (l) (mylength2-aux l 0))(defun mylength2-aux (l count) (if (endp l) count (mylength2-aux l (+ count 1))))
Note All recursive calls simply return the final value evaluated. No additional computations
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 32
Tail Recursion
Tail Recursion The final expression of a function is a recursive call No additional computations are done to that expression
– That is, return value is JUST the result of the recursive call
Lisp handles tail recursion efficiently Mylength is not tail recursive Mylength2 is tail recursive
Example: Write a function to produce N atoms with value ‘A’.
> (produce-list-of-a 5) ; Example call(A A A A A)
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 33
Produce-list-of-a Using dotimes
(defun produce-list-of-a (n) (let ((la NIL)) (dotimes (i n la) (push 'A la))))
Recursion, not tail recursive(defun produce-list-of-a (n) (if (= n 0) nil (cons 'A (produce-list-of-a (- n 1)))))
Recursion, with tail recursion(defun produce-list-of-a (n) (produce-list-of-a-aux n NIL))(defun produce-list-of-a-aux (n list-so-far) (if (= n 0) list-so-far (produce-list-of-a-aux (- n 1) (cons 'A list-so-far))))
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 34
Tower of Hanoi
Three pegs, S(start), T(temp), E(end) N disks Goal: Move disks from peg S to peg E Restriction: Larger disk can’t be placed on top of
smaller disk
S T E
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 35
Tower of Hanoi
Solution to Tower of Hanoi(defun hanoi-aux (n start end temp) (if (> n 1) (hanoi-aux (- n 1) start temp
end)) (print (list start end)) (if (> n 1) (hanoi-aux (- n 1) temp end
start)))(defun hanoi (n) (hanoi-aux n 'S 'E 'T))
Example Runs> (hanoi 2)
(S T) (S E) (T E) NIL
> (hanoi 3)(S E) (S T) (E T) (S E) (T S) (T E) (S E) NIL
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 36
&Optional
Produce-list-of-a function(s)(defun produce-list-of-a (n) (produce-list-of-a-aux n NIL))(defun produce-list-of-a-aux (n list-so-far) (if (= n 0) list-so-far (produce-list-of-a-aux (- n 1) (cons 'A list-so-far))))
Redefined with optional parameters(defun produce-list-of-a (n &optional list-so-far) (if (= n 0) list-so-far (produce-list-of-a (- n 1) (cons 'A list-so-far))))
Note: optional values are bound to NIL, by default
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 37
Optional Parameters (cont)
Solution to Hanoi(defun hanoi-aux (n start end temp) (if (> n 1) (hanoi-aux (- n 1) start temp
end)) (print (list start end)) (if (> n 1) (hanoi-aux (- n 1) temp end
start)))(defun hanoi (n) (hanoi-aux n 'S 'E 'T))
Revised with optional parameters(defun hanoi (n &optional (start 'S) (end 'E)
(temp 'T)) (if (> n 1) (hanoi (- n 1) start temp end)) (print (list start end)) (if (> n 1) (hanoi (- n 1) temp end start)))
Note: notice the syntax for initializing optional parameters
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 38
&Rest
&Rest - Specifies a variable number of arguments Example: Assume + only accepts 2 arguments. Define “Plus”,
a function that adds an arbitrary number of arguments)> (plus 2 3)> (plus 2 3 4 5 8)
Solution(defun plus (arg1 &rest other_args) (plus-aux arg1 other_args))(defun plus-aux (arg1 other_args) (if (null other_args) arg1 (plus-aux (+ arg1 (first other_args)) (rest other_args))))
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 39
Key Parameters
KEYword parameter Useful when function has MANY parameters Most are tied to default values
Examples> (rotate-list '(a b c d e)) ; rotate one element
right(E A B C D)
> (rotate-list '(a b c d e) :direction 'left)(B C D E A)
> (rotate-list '(a b c d e) :distance 2)(D E A B C)
> (rotate-list '(a b c d e) :direction 'left :distance 2)
(C D E A B)
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 40
&Key
Use &key to define Key parameters(defun rotate-list (l &key (direction 'right)
(distance 1)) (if (eq direction 'left) (rotate-list-left l distance) (rotate-list-right l distance)))
(defun rotate-list-right (l n) (if (zerop n) l (rotate-list-right (append (last l)
(butlast l)) (- n 1))))(defun rotate-list-left (l n) (if (zerop n) l (rotate-list-right (append (rest l) (list
(first l))) (- n 1))))
CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta 41
&Aux
&Aux keyword is used as a shorthand for Let* Produce-list-of-a using Let*
(defun produce-list-of-a (n) (let* ((la NIL)) (dotimes (i n la) (push 'A la))))
Using &Aux(defun produce-list-of-a (n &aux (la NIL)) (dotimes (i n la) (push 'A la)))