[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Assignments] [Labs]

Back to Laboratory Session: Scheme. On to Continuations, Continued.

**Held** Monday, February 8

**Summary**

- Getting the context of a function call from within the function
- Using continuations
- Tail recursion

**Contents**

**Handouts**

**Notes**

- I should be returning assignment 1
- I did not grade all of the problems.
- I realize that I don't spell-check my outlines (and sometimes fail to spell-check assignments), but I'd certainly like you to do so.
- My grading scheme is ``mark based''. A check is ``primarily correct''. A check minus is ``some errors''. A minus is ``substantially wrong''. A 0 is ``not done, or as bad as not done''. A check plus is ``some particularly good insight''. A plus is ``outstanding insight''. A check on every problem will get you a B.

- We'll spend some time discussing testing and timing, since so few of you got it right.

- An interesting aspect of Scheme is that it gives you access to what
are typically called
*continuations*. Continuations are functions that, in effect, represent ``the rest of the computation'' at a given point in the computation. - What do we mean by "the rest of the computation". Consider the
expression
(* 3 (- (+ A 4) 5))

- As you know, this is executed by
- adding A and 4
- subtracting 5 from the result
- multiplying 3 by the result of that computation

- So, the continuation of
`(- (+ A 4) 5)`

is ``multiply 3 by the result''. - Similarly, the continuation of
`(+ A 4)`

is ``subtract 5 from the result and then multiply 3 by that new result''. - Observe that we can write each of these as a lambda expression.
- ``multiply 3 by the result'':
`(lambda (x) (* 3 x))`

- ``subtract 5 from the result'':
`(lambda (y) (- y 5))`

- ``multiply 3 by the result'':
- We can use these continuations to rewrite the original expression as
((lambda (x) (* 3 x)) ((lambda (y) (- y 5)) (+ A 4)))

- Why is this useful? In part, it makes the order-of-evaluation somewhat clearer (albeit backwards). First you add A and 4, then you subtract 5, then you multiply by 3.
- Note that this is also getting fairly close to assembly code
(again, backwards assembly code). Compare it to
ADD A #4 STO TMP SUB TMP #5 STO TMP SUB #3 TMP

- In fact, Scheme compilers and interpreters always (almost always?) build such continuations as they compile Scheme programs.
- You can also program in such a fashion. This is typically called
*continuation passing style*or*CPS*. - How does this help you?
- It provides you with greater control over sequencing. Consider
`((read) - (read))`

. Which`(read)`

is done first? It turns out that it's up to the implementer. If you want the first one done first, you could write((lambda (first) ((lambda (second) (- first second)) (read))) (read))

- It gives you a different way of thinking about programming. As we'll see later, continuations can provide convenient mechanisms for writing recursive functions and for providing "functionally pure" input and output.
- In Scheme, you can get access to your current continuation. This permits some very interesting control possibilities which we will also discuss later.
- It lets you strengthen your understanding of lambda expressions.
- Andrew Appel says that CPS ``makes every aspect of control flow and
data flow explicit. It also has the advantage that it's closely
related to Church's lambda-calculus, which has a well-defined
semantics.'' (Andrew Appel,
*Compiling with Continuations*, Cambridge University Press, 1982, p. 2). - Continuations are ``natural'' if you think about what's going on
behind the scenes. For example, consider the following example
based on Appel's example in the previous reference.
When the predicate
(define (prodprimes n) (if (= n 1) 1 (if (prime? n) (* n (prodprimes (- n 1))) (prodprimes (- n 1)))))

`prime?`

is called, it is passed a*return address*. It must also produce a*return value*which the code at the return address uses. We can think of that as a function, which me might then name. - If you always use CPS, you can have a function return multiple values (by calling a continuation on multiple values).
- It's really nice for making recursive functions tail-recursive.

- It provides you with greater control over sequencing. Consider

*Hopefully, this is a topic that most of you know already.*

- One of the main complaints about recursion is that it wastes stacks space for recursive calls and program time for function calls and returns.
- If a function does nothing after every recursive call, then it is possible to implement the recursive function more efficiently, by using the same stack frame and simply jumping to the start of the procedure body.
- Functions that do nothing after recursive calls (other than returning
the result of the recursive call) are called
*tail-recursive*functions. - Scheme requires that tail recursive functions be implemented ``correctly'' (with an efficient call mechanism).
- Let's consider ways to write tail-recursive factorial and exponential procedures.
- Here are the recursive, but not tail-recursive, definitions.
;;; Compute the factorial of n (define (factorial n) (if (= n 0) 1 (* n (factorial (- n 1))))) ;;; Determine whether n is even. A helper function for expt. (define (even n) (= (modulo n 2) 0)) ;;; Computer the square of x. A helper function for expt. (define (square x) (* x x)) ;;; Compute x^n for integer n. Runs in O(log_2 n) time. (define (expt x n) (cond ((= n 0) 1) ((even n) (square (expt x (/ n 2)))) (#t (* x (expt x (- n 1)))))) ;;; Compute x^n for integer n. Runs in O(n) time. (define (ex x n) (if (= n 0) 1 (* x (ex x (- n 1)))))

- One option for factorial is to use the standard accumulator
method. Note that
`letrec`

is a way to define local functions.(define (factorial n) (letrec ((facthelper (lambda (n acc) (if (= n 0) acc (facthelper (- n 1) (* n acc)))))) (facthelper n 1)))

- However, this does the multiplications in the wrong order. While it's not significant in this case, it might be significant in other cases.
- We'll soon see how to use continuations to do it in the right order.

**History**

- Created Tuesday, January 19, 1999 as a blank outline.
- Filled in some details on Friday, February 5, 1999. Some of these details were taken from class 23 of CS302.98S.
- Move uncovered materials to the subsequent outline on Wednesday, February 10, 1999.

Back to Laboratory Session: Scheme. On to Continuations, Continued.

[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Assignments] [Labs]

**Disclaimer** Often, these pages were created ``on the fly'' with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.

This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS302/99S/Outlines/outline.07.html

Source text last modified Wed Feb 10 09:39:43 1999.

This page generated on Wed Feb 10 09:55:16 1999 by SiteWeaver. Validate this page.

Contact our webmaster at rebelsky@math.grin.edu