324 Lecture Week 6 - Winter 2006 ================================ Symbolic Differentiation ======================== Let's manipulate some `Calculus functions' symbolically. So we have a datatype of such functions, and corresponding representations. For simplicity let's start with: sin 'sin cos 'cos negative of a function f (list '- f) sum of two functions f and g (list f '+ g) Pattern Matching ---------------- There is a common Scheme library for manipulating data based on its structure. Such pattern matching is also important in many statically typed languages (e.g. ML, Haskell) and taken further in some logic languages (as "unification", e.g. in Prolog). We've also seen a form of it in Scheme macros (which operate on code, not runtime values). In DrScheme, choose one of the PLT languages that includes MzScheme. Then to load the matching library: (require (lib "match.ss")) Let's use it to differentiate our calculus functions: (define d/dx (lambda (s) (match s ; check the structure of s ('sin 'cos) ; pattern and result ('cos '(- sin)) (('- f) `(- ,(d/dx f))) ((f '+ g) `(,(d/dx f) + ,(d/dx g)))))) (d/dx '(sin + (- cos))) => (cos + (- (- sin))) Let's write a procedure to change (- (- f)) to f. But first, let's mention a convenient syntax for making and naming procedures. Syntax for (define p (lambda ... -------------------------------- Now that you understand that procedure names are really just variables that happen to have a procedure value, and that procedure values can exist without naming them, it's safe to introduce a short form for the common case of making and naming a procedure. (define (p x y) ; present p in the form you would call it body ...) is the same as (define p (lambda (x y) body ...) Simplify -------- Let's write simplify-- that if given a list of the form (- (- f)), returns f, otherwise simply returns what it's given. In lecture we did something like: (define (simplify-- s) (if (and (list? s) (>= (length s) 2) (eq? (car s) '-) (eq? (cadr s) '-)) ; cadr is short for car of cdr (cddr s) s)) ; another variant - R5RS specifies up to 4 a/d's This has some problems. Exercise: fix them. Deep Simplify ------------- To simplify (- (- f)) wherever it occurs, we recurse. We can do it bottom-up or top-down. Bottom up: (define (deep-simplify-- s) (if (list? s) (simplify-- (map deep-simplify-- s)) s)) Top down: Tutorial Exercise. Named let ========== Another form of let: (let name ((var val) ...) exp1 ...) Simplifies some recursion -- name is bound to a procedure we can call to perform recursion. (define print-list (lambda (list) (let list ((l list)) (if (not (null? l)) (begin (display (car l)) (newline) (list (cdr l))))))) (print-list '(a b c)) Continuations ============= Scheme supports the definition of arbitrary control structures with continuations. A "continuation" is a procedure that embodies the remainder of a program at a given point in the program. A continuation may be obtained at any time during the execution of a program. As with other procedures, a continuation is a first-class object and may be invoked at any time after its creation. Whenever it is invoked, the program immediately continues from the point where the continuation was obtained. Continuations allow the implementation of complex control mechanisms including explicit backtracking, multithreading, and coroutines. What is a continuation? ----------------------- During the evaluation of a Scheme expression, the implementation must keep track of two things: (1) what to evaluate and (2) what to do with the value. Consider the evaluation of (null? x) within the expression below. (if (null? x) (quote ()) (cdr x)) The implementation must first evaluate (null? x) and, based on its value, evaluate either (quote ()) or (cdr x). "What to evaluate" is (null? x), and "what to do with the value" is to make the decision which of (quote ()) and (cdr x) to evaluate and to do so. We call "what to do with the value" the continuation of a computation. Thus, at any point during the evaluation of any expression, there is a continuation ready to complete, or at least continue, the computation from that point. Let's assume that x has the value (a b c). Then we can isolate six continuations during the evaluation of (if (null? x) (quote ()) (cdr x)), the continuations waiting for: 1. the value of (if (null? x) (quote ()) (cdr x)), 2. the value of (null? x), 3. the value of null?, 4. the value of x, 5. the value of cdr, and 6. the value of x (again). The continuation of (cdr x) is not listed because it is the same as the one waiting for (if (null? x) (quote ()) (cdr x)). call-with-current-continuation (abbreviated call/cc) ------------------------------ call/cc must be passed a procedure p of one argument. call/cc constructs a concrete representation of the the current continuation and passes it to p. The continuation itself is represented by a procedure k. Each time k is applied to a value, it returns the value to the continuation of the call/cc application. This value becomes, in essence, the value of the application of call/cc. If p returns without invoking k, the value returned by the procedure becomes the value of the application of call/cc. Consider the simple examples below. (call/cc (lambda (k) (* 5 4))) => 20 (call/cc (lambda (k) (* 5 (k 4)))) => 4 (+ 2 (call/cc (lambda (k) (* 5 (k 4))))) => 6 In the first example, the continuation is obtained and bound to k, but k is never used, so the value is simply the product of 5 and 4. In the second, the continuation is invoked before the multiplication, so the value is the value passed to the continuation, 4. In the third, the continuation includes the addition by 2; thus, the value is the value passed to the continuation, 4, plus 2. Declaring a continuation globally can help one reuse it many times, which makes the 'rest of the computation' available for reuse. Taking the same example and modifying it a little. (define r #f) (+ 1 (call/cc (lambda (k) (set! r k) (k 3)))) => 4, as usual. But now we can call the continuation with any value: (r 3) ==> 4 (r 10) ==> 11 (r 0.1) ==> 1.1 The critical thing to understand here is that, this is not a function but a continuation. That can be illustrated by (+ 3 (* 10 (r 5))) ==> 6 Resuming a continuation abandons the context that surrounds it.