Programming Languages (CSC-302 98S)
Outline of Class 26: Scheme, Concluded
Held: Monday, April 6, 1998
- Any questions on Chris Haynes' lecture? I was disappointed that a few
of you weren't there and that only a few of you responded to his questions
(although I will admit that he was expecting a lot of knowledge).
- Any questions on
Due to some concerns about Easter weekend, the start of
Passover, and some pagan wedding, I've extended the due date until
- Today's brown-bag lunch is on The Design of C++. C++ is
an object-oriented language with a syntax not unlike Javas, but with
some very different design decisions. I encourage you to attend
(but I won't be there due to a prior commitment).
- One unique aspect of Scheme is that it gives you access to the
continuations that it develops. If, at any point in an expression,
(call/cc fun), it calls the function
using the current continuation as a parameter.
- Now, I'll admit that I've mislead you slightly. What is the
(+ 2 3) in
(* (+2 3) 3) if the whole expression is typed in
response to the prompt?
- It's not just "multiply by three".
- Rather, it's closer to "multiply by three, print the result, and go on
to the next expression to evalute".
- Why is this important? Because the last two parts of this are included
in the continuations given by
call/cc, and they significantly
- Consider the following
> (define id (lambda (x) x))
> (define (silly cont) (* 2 (+ 3 (cont 1))))
- What do we get if we evaluate
(silly id)? Eight. Why?
We apply the
id function to 1, giving 1, add 3, giving
4, and then multiply by 2, giving 8 .
- What do we get if we evaluate
(+ 2 (call/cc silly))?
Surprisingly, we get 3.
- What has happened? When we
(cont 1), we use the continuation of
"add 2, print, and go on to the next expression".
- In effect, we've thrown away the normal continuation of that
part of the expression.
- Continuations therefore provide a powerful escape clause for
Scheme programmers. Need to get out of a nested structure? Call
- Often, it is useful to provide a separate failure continuation
for when things go wrong.
- As you may have noted, it's useful to create local scopes
when building larger programs. Scopes typically introduce variables
and bind values to those variables.
- In Scheme, you create scopes with
which have the form
(let ((var1 val1)
- For example, we might get around the old "how many times do we
read a value" with
(let ((tmp (read)))
(* tmp tmp))
- This can also be useful for recursive functions (e.g., filtering)
(define (filter pred lst)
(if (null? lst) nil
(let ((first (car lst))
(rest (filter pred (cdr lst))))
(if (pred first)
(cons first rest)
- As you might guess, you can reuse variable names without
conflict. For example, the following evaluates to 25.
(let ((a 10))
(let ((a 5)) a)
- This can get confusing when you return lambda expressions. Note
that at the time we evaluate the lambda, the second a appears to
no longer exist.
(let ((a 10)
(fun (let ((a 5)) (lambda () a))))
(+ a (fun)))
let is not a necessary construct. It
is, in effect, a shorthand for lambda abstraction and application.
let clause above might be written
((lambda (var1 ... body)
val1 val2 ... valn)
- That is, plug in each of the values for each of the corresponding
variables in the body.
- Scheme also provides a number of operations for fooling with memory.
(set! var exp)
stores a value in the memory location corresponding to a variable.
- It is different from (define var exp), which also does allocation.
- It is different from (let ((var exp)) body)), which also does allocation
and provides scoping.
(set-car! var exp)
sets the first part of a cons cell.
- It is different from
(define var (cons exp (cdr var)))
which allocates a new cons cell.
- It should be different from
(set! var (cons exp (cdr var)))
(set-cdr! var exp) does something similar for the cdr.
- What are some expressions we might write to investigate the effects of