320 likes | 430 Vues
Explore the use of control blocks such as progn, block, and tagbody in Common Lisp to manage code execution flow effectively and elegantly. Learn to build branches, make sudden exits, and encapsulate functions within 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