220 likes | 412 Views
Advanced Scheme. Continuations. Continuations. continuations allow you to save a computation at any place in its execution. In scheme, use the built-in function: call-with-current-continuation Can abbreviate as call/cc in most versions of scheme (including Dr Scheme)
E N D
Advanced Scheme Continuations
Continuations • continuations allow you to save a computation at any place in its execution. • In scheme, use the built-in function: call-with-current-continuation • Can abbreviate as call/cc in most versions of scheme (including Dr Scheme) • Can think of a continuation as a bookmark into a computation
call/cc • Call/cc is a built-in function • Takes as an argument a procedure of one parameter • Call/cc calls the passed function and binds the continuation to the parameter of the function. • When the parameter of the procedure is used in its body, computation jumps to the continuation and passes the argument to that point of the continuation
example • In this example, call/cc is called after the + 1 computation is done • The continuation at this point is bound to k and passed to the lambda • k is used in the call (k 3) • At this point the continuation is executed, ie execution is restarted right after the (+ 1 and the argument of k (ie 3) is passed as the result of the continuation. • In other words it ignores any computation that occurred after the continuation started, but before the continuation was applied. (+ 1 (call/cc (lambda (k) (+ 2 (k 3))))) 4 >
example The continuation captures the computation. In this case it occurs right after the + 1, so when the continuation is used it will return its value to the +1 computation. (+ 1 (call/cc (lambda (k) (+ 2 (k 3))))) 4 >
Call/cc receives as an argument a function that has one parameter When the parameter is used ("called") it continues execution, returning to the execution it's parameter (3 in this case) This function is immediately called. It is passed the "continuation" example (+ 1 (call/cc (lambda (k) (+ 2 (k 3))))) 4 >
Example 2 • In this example, call/cc is called after the + 1 computation is done, but there is more computation (ie values) after the call/cc function • The continuation at the call/cc is bound to k and passed to the lambda • k is used in the call (k 3) • At this point the continuation is executed, ie execution is restarted right after the (+ 1 and the argument of k (ie 3) is passed as the result of the continuation. • But there is more computation to be done after the call/cc, so the 4 and 10 are added to the result also! (+ 1 (call/cc (lambda (k) (+ 2 (k 3)))) 4 10) 18 >
Example 2 The continuation captures the computation in the middle. In this case it occurs right after the + 1, but before the adding of 4 and 10. (+ 1 (call/cc (lambda (k) (+ 2 (k 3)))) 4 10) 18 > When k (the continuation) is called, we pick up at the + 1, pass in the argument of k (I.e., 3) and then finish the computation (add 4 and 10)
Escaping Continuations • Continuations can be used to escape from computations • Can conceptualize as “jumping” out of a procedure
Escaping Continuations: example (define list-product (lambda (s) (letrec ((multList (lambda (s) (if (null? s) 1 (* (car s) (multList(cdr s))) )))) (multList s)) ) ) • Works, but inefficient • What if have a long list and end item is 0? • Will return through the entire list and multiply each item by 0!
Escaping Continuations: example • One solution: tail recursion • Another solution: continuation (define list-product (lambda (s) (call/cc (lambda (exit) (letrec ((multList (lambda (s) (if (null? s) 1 (if (= (car s) 0) (exit 0) (* (car s) (multList(cdr s))) ))))) (multList s)) )) ))
Escaping Continuations: example 2 • Find an element in a list • Use a built-in function for-each • return does not exist in scheme • (and yes, I know that we could use a recursive function instead) (define find-item (lambda (wanted? theList) (for-each (lambda (element) (if (wanted? element) (return element))) theList) #f)) To call: (find-item (lambda (x) (equal? x 'a)) '(b c d a e f g)) This code won’t run! The function return doesn’t exist! How can we return?
Escaping Continuations: example 2 • Instead of return use a continuation (define find-item (lambda (wanted? theList) (call/cc (lambda (return) (for-each (lambda (element) (if (wanted? element) (return #t))) theList) #f)))) The return becomes a continuation
Escaping Continuations: example 3deep recursion ; adds a number to the product of a list of numbers (define product+ (lambda (n nums) (let ((receiver (lambda (exit-on-zero) (letrec ((product (lambda (nums) (cond ((null? nums) 1) ((number? (car nums)) (cond ((zero? (car nums))(exit-on-zero 0)) (else (* (car nums) (product (cdr nums)))))) (else (* (product (car nums)) (product (cdr nums)))))))) (+ n (product nums)))))) (call/cc receiver)))) The exit-on-zero becomes a continuation
Saving Continuations • Can save a continuation and use it later (define r #f) (+ 1 (call/cc (lambda (k) (set! r k) (k 3)))) 4 > (r 1) 2 > (r 10) 11 > (r 32) 33 > (+ 3 4 (r 20)) 21 >
Background: writeln ;; first must define writeln (define writeln (lambda lst ;; need a helper function to deal with the lst; a recursive call to writeln ;; will place lst into another list (letrec ((helper (lambda (newlst) (if (not (null? newlst)) (begin (display (car newlst)) (display #\space) (helper (cdr newlst))) (newline))))) (helper lst))))
Looping with Continuations ; loops with continuations (define countdown (lambda (n) (writeln "This only appears once") (let ((pair (message "Exit" (attempt (message "Enter" n))))) (let ((v (car pair)) (returner (cadr pair))) (writeln " The non-negative-number: " v) (if (positive? v) (returner (list (sub1 v) returner)) (writeln "Blastoff!")))))) (define message (lambda (direction value) (writeln " " direction "ing attempt with value: " value) value)) (define attempt (lambda (n) (let ((receiver (lambda (proc)(list n proc)))) (call/cc receiver)))) • Welcome to DrScheme, version 209. • Language: Graphical (MrEd, includes MzScheme). • (countdown 10) • This only appears once • Enter ing attempt with value: 10 • Exit ing attempt with value: (10 #<continuation>) • The non-negative-number: 10 • Exit ing attempt with value: (9 #<continuation>) • The non-negative-number: 9 • Exit ing attempt with value: (8 #<continuation>) • The non-negative-number: 8 • Exit ing attempt with value: (7 #<continuation>) • The non-negative-number: 7 • Exit ing attempt with value: (6 #<continuation>) • The non-negative-number: 6 • Exit ing attempt with value: (5 #<continuation>) • The non-negative-number: 5 • Exit ing attempt with value: (4 #<continuation>) • The non-negative-number: 4 • Exit ing attempt with value: (3 #<continuation>) • The non-negative-number: 3 • Exit ing attempt with value: (2 #<continuation>) • The non-negative-number: 2 • Exit ing attempt with value: (1 #<continuation>) • The non-negative-number: 1 • Exit ing attempt with value: (0 #<continuation>) • The non-negative-number: 0 • Blastoff!
Named let expressions • Background: a named let: (define test (lambda () (letrec ((countdown (lambda (i) (if (= i 0) 'liftoff (begin (display i) (newline) (countdown (- i 1))))))) (countdown 10)))) (define test (lambda () (letrec ((countdown (lambda (i) (if (= i 0) 'liftoff (begin (display i) (newline) (countdown (- i 1)))) ))) (countdown 10)) ))
First statement in body of countdown First statement in body of countdown Second statement in body of countdown Second statement in body of countdown Named let expressions • This is an infinite loop: (define test (lambda () (letrec ((countdown (lambda (i) (if (= i 0) 'liftoff (begin (display i) (newline) (countdown (- i 1)))) (countdown 20)))) (countdown 10)) )) (define test (lambda () (let countdown ((i 10)) (if (= i 0) 'liftoff (begin (display i) (newline) (countdown (- i 1)))) (countdown 20) ))) The second statement produces an infinite loop
Coroutines (define hefty-computation (lambda (do-other-stuff) (letrec ((junk (lambda (n) (display "Hefty computation (a). ") (display n) (newline) (set! do-other-stuff (call/cc do-other-stuff)) (display "Hefty computation (b). ") (display n) (newline) (set! do-other-stuff (call/cc do-other-stuff)) (display "Hefty computation (c). ") (display n) (newline) (set! do-other-stuff (call/cc do-other-stuff)) (if (> n 0) (junk (- n 1))))) ) (junk 5))))
Coroutines ;; notationally displays a clock ;; uses a named let (define superfluous-computation (lambda (do-other-stuff) (let loop() (for-each (lambda (graphic) (display graphic) (newline) (set! do-other-stuff (call/cc do-other-stuff))) '("Straight-up" "Quarter after" "Half past" "Quarter till")) (loop)))) (hefty-computation superfluous-computation)
Coroutines ;; notationally displays a clock ;; uses a letrec (define superfluous-computation (lambda (do-other-stuff) (letrec ((loop (lambda () (for-each (lambda (graphic) (display graphic) (newline) (set! do-other-stuff (call/cc do-other-stuff))) '("Straight-up" "Quarter after" "Half past" "Quarter till")) (loop) ;; this causes infinite recursion in the local function ))) (loop) ;; the single statement in the letrec ))) (hefty-computation superfluous-computation)