320 likes | 419 Views
Functional Programming. 04 Control. Control-Blocks. Common Lisp has 3 basic operators for creating blocks of code progn block tagbody If ordinary function calls are the leaves of a Lisp program, these operators are used to build the branches. Control-Blocks.
E N D
Functional Programming 04 Control
Control-Blocks • Common Lisp has 3 basic operators for creating blocks of code • progn • block • tagbody • If ordinary function calls are the leaves of a Lisp program, these operators are used to build the branches
Control-Blocks • > (progn (format t “a”) (format t “b”)(+ 11 12))ab23 -> only the value of the last expression is returned
Control-Blocks • > (block head (format t “Here we go.”) (return-from head ‘idea) (format t “We’ll never see this.”))Here we go.IDEA • Calling return-from allows your code to make a sudden but graceful exit from anywhere in a body of code • Expressions after the return-from are not evaluated
Control-Blocks • > (block nil (return 27))27 • > (dolist (x ‘(a b c d e)) (format t “~A “ x) (if (eql x ‘c) (return ‘done)))A B CDONE
Control-Blocks • The body of a function defined with defun is implicitly enclosed in a block with the same name as the function • (defunfoo()(return-fromfoo27)) • (defunread-integer(str)(let((accum0))(dotimes(pos(lengthstr))(let((i(digit-char-p(charstrpos))))(ifi(setfaccum(+(* accum 10) i)) (return-from read-integer nil))))accum))
Control-Blocks • Within tagbody, you can use go • > (tagbody (setf x 0)top (setf x (+ x 1)) (format t “~A “ x) (if (< x 10) (gotop)))1 2 3 4 5 6 7 8 9 10 • This operator is mainly something that other operators are built upon, not something you would use yourself Ugly code!!
Control-Blocks • How to decide which block construct to use? • Nearly all the time you’ll use progn • If you want to allow for sudden exits, use block • Most programmers will never use tagbody explicitly
Control-Context • let • Takes a body of code • Allows us to establish variables for use within the body • > (let ((x 7) (y 2)) (format t “Number”) (+ x y))Number9
Control-Context • >((lambda (x) (+ x 1)) 3)4 • ((lambda (x y) (format t “Number”) (+ x y))72)
Control-Context • One let-created variable can’t depend on other variables created by the same let • (let ((x 2) (y (+ x 1))) (+ x y)) • ((lambda (x y) (+ x y)) 2 (+ x 1)) • > (let* ((x 1) (y (+ x 1))) (+ x y))3 equivalent
Control-Context • A let* is functionally equivalent to a series of nested lets • (let ((x 1)) (let ((y (+ x 1))) (+ x y))) • In both let and let*, initial values default to nil • > (let (x y) (list x y))(NIL NIL)
Control- Conditionals • (if (oddp that) (progn (format t “Hmm, that’s odd.”) (+ that 1))) • (when (oddp that) (format t “Hmm, that’s odd.”) (+ that 1)) equivalent
Control- Conditionals • (if <test> <then form> <else form>) • (if <test> <then form>)(when <test> <then form>) • (if <test> nil <else form>) (unless <test> <else form>)
Control- Conditionals • (defun our-member (obj lst) (if (atom lst) nil (if (eql (car lst) obj)lst (our-member obj (cdr lst)))) • (defun our-member (obj lst) (cond ((atom lst) nil) ((eql (car lst) obj) lst) (t (our-member obj (cdr lst))))) A Common Lisp implementation will probably implement cond by translating it to the “if” format equivalent
Control- Conditionals • cond • (cond (<test 1> <consequent 1-1> …) (<test 2> <consequent 2-1> …) … (<test m> <consequent m-1> …));cond • The conditions are evaluated in order until one of them returns true • When one condition returns true, the expressions associated with it are evaluated in order, and the value of the last is returns as the value of the cond • > (cond (99)) ; if there are no expressions after the successful condition99; , the value of the condition itself is returned.
Control- Conditionals • case • (case <key form> (<key 1> <consequent 1-1> …) (<key 2> <consequent 2-1> …) ... (<key m> <consequent m-1> …)) ;case
Control- Conditionals • (defun month-length (mon) (casemon ((jan mar may julaugoctdec) 31) ((aprjunseptnov) 30) (feb (if (leap-year) 29 28)) (otherwise “unknown month”))) • Keys • Are treated as constants • Will not be evaluated • > (case 99 (99)) The successful clause contains only keysNIL
Control- Iteration • do • (do ((<parameter 1> <initial value 1> <update form 1>) (<parameter 2> <initial value 2> <update form 2>) … (<parameter n> <initial value n> <update form n>)) (<termination test> <intermediate forms, if any> <result form>) <body>) ;do • > (let ((x ‘a)) (do ((x 1 (+ x 1)) (y x x)) ((> x 5)) (format t “(~A ~A) “ x y)))(1 A) (2 1) (3 2) (4 3) (5 4) ;on each iteration, x gets its previousNIL ;value plus 1; y also gets the previous ;value
Control- Iteration • do* • Has the same relation to do as let* does to let • > (do* ((x 1 (+ x 1)) (y x x)) ((> x 5)) (format t “(~A ~A) “ x y))(1 1) (2 2) (3 3) (4 4) (5 5)NIL
Control- Iteration • dolist • > (dolist (x ‘(a b c d) ‘done) (format t “~A “ x))A B C DDONE • dotimes • > (dotimes (x 5 x) ; for x = 0 to 5-1, return x (format t “~A “ x))0 1 2 3 4 5
Control- Multiple values • In Common Lisp, an expression can return zero or more values • E.g., get-decoded-time returns the current time in nine values • value • Returns multiple values • > (values ‘a nil (+ 2 4))ANIL6
Control- Multiple values • > ((lambda ( ) (values 1 2) ))1;2 • If something is expecting only one value, all but the first will be discarded • > (let ((x (values 1 2))) x)1
Control- Multiple values • > (values) returns no value • > (let ((x (values))) x)NIL • Use multiple-value-bind to receive multiple values • > (multiple-value-bind (x y z) (values 1 2 3) (list x y z))(1 2 3) • > (multiple-value-bind (x y z) (values 1 2) (list x y z))(1 2 NIL)
Control- Multiple values • > (multiple-value-bind (s m h) (get-decoded-time) (format nil “~A:~A:~A” h m s))“4:32:13” • We can pass on multiple values as the arguments to a second function using multiple-value-call • > (multiple-value-call #’+ (values 1 2 3))6 • multiple-value-list is like using multiple-value-call with #’list as the first argument • > (multiple-value-list (values ‘a ‘b ‘c))(A B C)
Control-Aborts • catch and throw • (defun super ( ) (catch ‘abort (sub) (format t “We’ll never see this.”)))(defun sub ( ) (throw ‘abort 99)> (super)99
Control- Example: Date arithmetic (defconstant month #(0 31 59 90 120 151 181 212 243 273 304 334 365)) (defconstant yzero 2000) (defun leap? (y) (and (zerop (mod y 4)) (or (zerop (mod y 400)) (not (zerop (mod y 100))))))
Control- Example: Date arithmetic (defun date->num (d m y) (+ (- d 1) (month-num m y) (year-num y))) (defun month-num (m y) (+ (svref month (- m 1)) (if (and (> m 2) (leap? y)) 1 0))) (defun year-num (y) (let ((d 0)) (if (>= y yzero) (dotimes (i (- y yzero) d) (incf d (year-days (+ yzero i)))) (dotimes (i (- yzero y) (- d)) (incf d (year-days (+ y i))))))) (defun year-days (y) (if (leap? y) 366 365))
Control- Example: Date arithmetic (defun num->date (n) (multiple-value-bind (y left) (num-year n) (multiple-value-bind (m d) (num-month left y) (values d m y)))) (defunnum-year (n) (if (< n 0) (do* ((y (- yzero 1) (- y 1)) (d (- (year-days y)) (- d (year-days y)))) ((<= d n) (values y (- n d)))) (do* ((y yzero (+ y 1)) (prev 0 d) (d (year-days y) (+ d (year-days y)))) ((> d n) (values y (- n prev))))))
Control- Example: Date arithmetic (defunnum-month (n y) (if (leap? y) (cond ((= n 59) (values 2 29)) ((> n 59) (nmon (- n 1))) (t (nmon n))) (nmon n))) (defunnmon (n) (let ((m (position n month :test #’<))) (values m (+ 1 (- n (svref month (- m 1))))))) (defun date+ (d m y n) (num->date (+ (date->num d m y) n)))
Control- Example: Date arithmetic • > (mapcar #’leap? ‘(1904 1900 1600))(T NIL T) • > (multiple-value-list (date+ 17 12 1997 60))(15 2 1998)
Control • Homework (Due April 7) • Rewrite month-mon to use case instead of svref • Define a single recursive function that returns, as two values, the maximum and minimum elements of a vector