CSCI 2210: Programming in Lisp Procedures CSCI 2210: Programming in Lisp Procedures CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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)) 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
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
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) > (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 x1 originy copied to y1 2 copied to x2 2 copied to y2 CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
Scope Consider Value of C is copied to A > (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) (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
Let Let = Lisp’s way of defining local variables Example (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
Let vs. Let* Let - Parallel evaluation Let* - Sequential evaluation > (setf x ’outside) OUTSIDE > (let ((x ’inside) (y x)) (list x y)) (INSIDE OUTSIDE) Let* - Sequential evaluation > (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
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)) / 2a x2 = (-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
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
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
Relationships Bet Equality Pred’s F = EQL A EQ D E B C 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) CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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
EQUAL vs. EQL vs. = EQUAL vs. EQL EQUAL vs. = C 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 A X C Y B A Z CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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
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
If, When, Unless If When Unless (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
Cond Cond format Semantics (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
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
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
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
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
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
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 CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
DoTimes DOTIMES is Lisp’s way of doing iteration C/C++ Lisp 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
Recursively Calculating Factorial Mathematical Definition of Factorial C/C++ Implementation long 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))))) CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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 = 24 End (recursive-factorial 4), returns 24 CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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
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
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) :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
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
Recursive Definition of Length (defun mylength (l) (if (endp l) (+ 1 (mylength (rest l))))) Alternate definition (defun mylength2 (l) (mylength2-aux l 0)) (defun mylength2-aux (l count) 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
Tail Recursion Tail Recursion Lisp handles tail recursion efficiently 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
Produce-list-of-a Using dotimes Recursion, not tail recursive (defun produce-list-of-a (n) (let ((la NIL)) (dotimes (i n la) (push 'A la)))) Recursion, not tail recursive (if (= n 0) nil (cons 'A (produce-list-of-a (- n 1))))) Recursion, with tail recursion (produce-list-of-a-aux n NIL)) (defun produce-list-of-a-aux (n list-so-far) list-so-far (produce-list-of-a-aux (- n 1) (cons 'A list-so-far)))) CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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
Tower of Hanoi Solution to Tower of Hanoi Example Runs (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) (T S) (T E) NIL CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
&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) (produce-list-of-a Note: optional values are bound to NIL, by default CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
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)) (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
&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
Key Parameters KEYword parameter Examples 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
&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) (rotate-list-right (append (rest l) (list (first l))) CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta
&Aux &Aux keyword is used as a shorthand for Let* Procedures &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))) CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta CSCI 2210 - Programming in Lisp; Instructor: Alok Mehta