324 Lecture Week 7 - Winter 2006 ================================ Continuations (continued) ============= Recall call-with-current-continuation (call/cc) Here we show the use of call/cc to provide a nonlocal exit from a recursion. (define product (lambda (ls) (call/cc (lambda (break) (let f ((ls ls)) (cond ((null? ls) 1) ((= (car ls) 0) (break 0)) (else (display (car ls)) (newline) (* (car ls) (f (cdr ls)))))))))) (product '(1 2 3 4 5)) => 120 or 1\n2\n3\n4\n5\n120 (product '(7 3 8 0 1 9 5)) => 0 or 7\n3\n8\n0 The nonlocal exit allows product to return immediately, without performing the pending multiplications, when a zero value is detected. Continuations used in this manner are not always so puzzling. Consider the following definition of factorial that saves the continuation at the base of the recursion before returning 1, by assigning the top-level variable retry. (define retry #f) (define factorial (lambda (x) (if (= x 0) (call/cc (lambda (k) (set! retry k) 1)) (* x (factorial (- x 1)))))) With this definition, factorial works as we expect factorial to work, except it has the side effect of assigning retry. (factorial 4) 24 (retry 1) 24 (retry 2) 48 The continuation bound to retry might be described as "Multiply the value by 1, then multiply this result by 2, then multiply this result by 3, then multiply this result by 4." If we pass the continuation a different value, i.e., not 1, we will cause the base value to be something other than 1 and hence change the end result. (retry 2) 48 (retry 5) 120 This mechanism could be the basis for a breakpoint package implemented with call/cc; each time a breakpoint is encountered, the continuation of the breakpoint is saved so that the computation may be restarted from the breakpoint (more than once, if desired). light-weight processes ---------------------- Continuations may be used to implement various forms of multitasking. The simple "light-weight process" mechanism defined below allows multiple computations to be interleaved. Since it is nonpreemptive, it requires that each process voluntarily "pause" from time to time in order to allow the others to run. (define lwp-list '()) ; to be used as a queue of processes (define schedule (lambda (thunk) (set! lwp-list (append lwp-list (list thunk))))) (define start (lambda () (let ((p (car lwp-list))) (set! lwp-list (cdr lwp-list)) (p)))) (define pause (lambda () (call/cc (lambda (k) (schedule (lambda () (k #f))) (start))))) The following light-weight processes cooperate to print an infinite sequence of lines containing "hey!" (be fast on your Ctrl+C!). (schedule (lambda () (let f () (pause) (display "h") (f)))) (schedule (lambda () (let f () (pause) (display "e") (f)))) (schedule (lambda () (let f () (pause) (display "y") (f)))) (schedule (lambda () (let f () (pause) (display "!") (f)))) (schedule (lambda () (let f () (pause) (newline) (f)))) (start) => hey! hey! hey! hey! ...