; We write some code in CPS (continuation-passing style) ; As we did in our first representation of ; environments, we represent each continuation by a ; Scheme procedure. ; We see that make-k and apply-k are very simple. ; ; Then, as we did with our second representation of environments, ; we use define-datatype to define a continuation variant record ; type where each specific continuation is one of the variants. ;----------------------------------------------- ; CONTINUATIONS REPRESENTED BY SCHEME PROCEDURES ;----------------------------------------------- (define apply-k (lambda (k v) (k v))) (define make-k ; lambda is the "real" continuation (lambda (v) v)) ; constructor here. ;; Some procedures to transform to CPS. ;; When we get the answer without a substantial call, ;; we apply k to that answer. ;; When we make a call to a substantial procedure, ;; we must provide a continuation. (define fact ; normal version, NOT tail-recursive (lambda (n) (if (zero? n) 1 (* n (fact (- n 1)))))) (define fact-cps (lambda (n k) (if (zero? n) (apply-k k 1) (fact-cps (- n 1) (make-k (lambda (v) (apply-k k (* n v)))))))) (fact-cps 5 (make-k list)) (fact-cps 6 (make-k (lambda (v) (* 10 v)))) (trace make-k apply-k fact-cps list) (fact-cps 5 (make-k list)) (define list-copy-cps (lambda (L k) (if (null? L) (apply-k k '()) (list-copy-cps (cdr L) (make-k (lambda (copied-cdr) (apply-k k (cons (car L) copied-cdr)))))))) (list-copy-cps '(1 2 3) (make-k reverse)) ; memq is a built-in procedure, so we could treat it as primitive. ; Here I treat it as substantial, in order to enhance the CPS learning process. (define memq-cps (lambda (sym ls k) (cond [(null? ls) ; fill it in [(eq? (car ls) sym) ; fill it in [else (memq-cps sym (cdr ls) k)]) )) (memq-cps 'a '(b c a d) (make-k list)) (memq-cps 'a '( b c d) (make-k not)) (define intersection ; intersection of two sets of symbols. (lambda (los1 los2) (cond [(null? los1) '()] [(memq (car los1) los2) (cons (car los1) (intersection (cdr los1) los2))] [else (intersection (cdr los1) los2)]))) ; I could do this in a style that more closely mirrors the layout ; of the above code. But I decided instead to write only one call to intersection-cps. ; ; Recall: ;; When we get the answer without a substantial call, we apply k to that answer. ;; When we make a call to a substantial procedure, we must provide a continuation. (define intersection-cps (lambda (los1 los2 k) (if (null? los1) ; fill it in ))) (intersection-cps '(a d e g h) '(s f c h b r a) (make-k list)) (trace intersection-cps make-k apply-k) (intersection-cps '(a d e g h) '(s f c h b r a) (make-k list)) ; ------------------------------------------------------ ; CONTINUATIONS AS VARIANT RECORDS using define-datatype. ; ------------------------------------------------------ (load "chez-init.ss") (define scheme-value? (lambda (x) #t)) (define 1st car) (define 2nd cadr) (define 3rd caddr) ; A helper procedure that will be useful: (define exp? ; Is obj a lambda-calculus expression? This uses (lambda (obj) ; our original simple definition of lc-expressions. (or (symbol? obj) (and (list? obj) (or (and (= (length obj) 3) (eq? (1st obj) 'lambda) (list? (2nd obj)) (= (length (2nd obj)) 1) (symbol? (caadr obj)) (exp? (3rd obj))) (and (= (length obj) 2) (exp? (1st obj)) (exp? (2nd obj)))))))) (define-datatype continuation continuation? [init-k] ; These first continuation variants need no fields. [list-k] [not-k] [fact-k (n integer?) (k continuation?)] [copy-k (car-L scheme-value?) (k continuation?)] ) (define apply-k (lambda (k v) (cases continuation k [init-k () v] [list-k () (list v)] [not-k () (not v)] [fact-k (n k) (apply-k k (* n v))] [copy-k (car-L k) (apply-k k (cons car-L v))] ))) (define fact-cps (lambda (n k) (if (zero? n) (apply-k k 1) (fact-cps (- n 1) (fact-k n k))))) (fact-cps 5 (init-k)) (fact-cps 6 (list-k)) (define list-copy-cps (lambda (L k) (if (null? L) (apply-k k '()) (list-copy-cps (cdr L) (copy-k (car L) k))))) (list-copy-cps '(1 2 3) (list-k)) (define memq-cps (lambda (sym ls k) (cond [(null? ls) (apply-k k #f)] [(eq? (car ls) sym) (apply-k k #t)] [else (memq-cps sym (cdr ls) k)]) )) (memq-cps 'a '(b c a d) (list-k)) (memq-cps 'a '( b c d) (not-k)) ; Copy the previous intersection-cps here, and rewrite ; it using data-structure continuations. (intersection-cps '(a d e g h) '(s f c h b r a) (list-k)) (trace intersection-cps apply-k list-k int-memq-k cdr-intersection-k) (intersection-cps '(a d e g h) '(s f c h b r a) (list-k)) ; This is my solution to the free-vars problem from A10. ; It was for the original lambda-calculus expressions where lambdas ; have only one parameter and applications have only one operand. (define free-vars ; convert to CPS. We will first convert (lambda (exp) ; union and remove. (cond [(symbol? exp) (list exp)] [(eq? (1st exp) 'lambda) (remove (car (2nd exp)) (free-vars (3rd exp)))] [else (union (free-vars (1st exp)) (free-vars (2nd exp)))]))) (define free-vars-cps ; convert to CPS (lambda (exp k) (cond [(symbol? exp) ;fill it in ] [(eq? (1st exp) 'lambda) ; fill it in ] [else ; fill it in ]))) (free-vars-cps '(a (b ((lambda (x) (c (d (lambda (y) ((x y) e))))) f))) (init-k)) (trace free-vars-cps free-vars-lambda-k rator-k rand-k union-cps remove-cps cdr-union-k union-memq-k rem-cdr-k apply-k) (free-vars-cps '(a (b ((lambda (x) (c (d (lambda (y) ((x y) e))))) f))) (init-k)) (define union-cps ; assumes that both arguments are sets of symbols (lambda (s1 s2 k) (if (null? s1) ; fill it in ))) (union-cps '(3 1 11 6 8 4) '(5 1 8 9 2) (make-k list)) (define remove-cps ; removes the first occurrence of element in ls (lambda (element ls k) (if (null? ls) ; fill it in ))) (remove-cps 'a '(b c e a d a a ) (init-k)) (remove-cps 'b '(b c e a d a a ) (init-k))