Professional Documents
Culture Documents
Slides12 Continuations PDF
Slides12 Continuations PDF
Lecture 12
Stephen Brookes
direct-style
A function of type t1 -> t2
can be applied to an argument of type t1,
and returns a value of type t2.
A direct-style function
evaluates its argument, returns a result.
continuation-style
A continuation-style function of type t1 -> (t2 -> a) -> a!
can be applied to an argument of type t1 !
and a continuation of type t2 -> a, !
where a is a type of answers. !
!
Instead of returning, !
it passes a value to its continuation,
to get an answer.
continuation?
A function that represents
Expects to be called
direct
cps
factorial
(* fact : int -> int *)
fun fact n = !
if n=0 then 1 else n * fact(n-1)
(* fact : int -> (int -> a) -> a *)
direct
style
not tail recursive
continuation
style
fun fact n k = !
if n=0 then k 1 else fact(n-1)(fn x => k(n * x))
tail recursive
examples
fact 3 (fn x => x) = 6
fact 3 Int.toString = 6
fact 3 (fn y -> fact y Int.toString) = 720
specs
(* fact : int -> int *)
(* REQUIRES n 0 *)
(* ENSURES fact(n) = n! *)
there is an integer v!
such that fact(n) = v,!
and v is equal to n!
(* REQUIRES n 0 *)
(* ENSURES fact n k = k(n!) *)
there is an integer v!
such that fact n k = k v,!
and v is equal to n!
proof
Let ans be a type, and let P(n) be:
For all k : int -> ans,
fact n k = k(fact n)
polymorphic
The cps factorial has a polymorphic type
int -> (int -> a) -> a
cps
When applied, a cps function calls its
continuation at most once
not cps
fun fact n k =
if n=0 then k 1 else n * fact(n-1) k
fact : int -> (int -> int) -> int
fact 3 (fn m => m+1) = 12
not cps
fun fact n k =
if n=0 then 1 else fact(n-1) (fn x => k(x*n))
fact : int -> (int -> int) -> int
fact 3 (fn m => m+1) = 1
counting a tree
count : int tree -> int
count : int tree -> (int -> a) -> a
fun count Empty = 0
| count (Node(l,x,r)) = count l + x + count r
fun count Empty k = k 0
| count (Node(l, x, r)) k =
count l (fn m => count r (fn n => k(m+x+n))
not cps
fun count Empty k = k 0
| count (Node(left, x, right)) k =
count left k + x + count right k
Why not?
Whats its type?
What goes wrong?
tail recursion
A recursive function definition is tail recursive
if each recursive call is in tail position,
the last thing the function does before returning.
fun fact n =!
if n=0 then 1 else n * fact(n-1)
fun factacc(n, a) =!
if n=0 then a else factacc(n-1, n*a)
fun fact n k = !
if n=0 then k 1 else fact(n-1) (fn x => k(n * x))
When a function is called, the argument values get pushed onto a stack, !
with a return address for the value returned by the call. !
!
For tail calls, we can re-use the same stack frame for arguments
and leave the return address unchanged: the result of the tail call !
needs to get returned to the original call site.
Execution of factacc(3, 1)
backtracking
Many search algorithms backtrack from a
dead end to an earlier choice point to
explore further alternatives
n queens
Queens attack on row, column, or diagonal
Task: put n queens safely on an n-by-n board
before we start
Number of ways to put 8 queens on board
64 * 63 * 62 * 61 * 60 * 59 * 58 *57
8*7*6*5*4*3*2*1
> 4,000,000,000
8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 40,320
92
PRICELESS
board
type row = int!
type col = int!
type pos = row * col!
type sol = pos list
threat
threat : pos * pos -> bool
conflict
(* conflict : pos * pos list -> bool *)
fun conflict (p, nil) = false!
| conflict (p, q::qs) = threat(p, q) orelse conflict(p, qs)
1
q2
1
q5
q4
q3
q1
safe
safe : pos list -> bool
fun safe [ ] = true
| safe (q::qs) = if conflict(q, qs)
then false
else safe(qs)
safe [q1,...,qk] = true
iff for all ij,
qi and qj are on different
rows, columns and diagonals
solutions
qs is a
partial solution
for rows 1 through i
if
safe qs = true &
map (fn (i, j) => i) qs = [i, ..., 1]
A full solution to the n-queens problem
is a partial solution qs for rows 1 through n
such that every (i,j) in qs has 1 j n
failure continuations
Many search algorithms can be solved elegantly
using failure continuations
strategy
A recursive function that maintains
try
try : int * row * col list * sol -> (unit -> sol option) -> sol option
try(n, i, A, qs) fc
REQUIRES: 1 i n and A is a sublist of [1,. . .,n] !
& qs is a partial solution for rows 1 through i-1 !
!
ENSURES:!
try (n, i, A, qs) fc = SOME(qs)
!
where qs is a full solution extending qs!
with a queen in row i at a column in A, if there is one!
try (n, i, A, qs) fc = fc( )
!
otherwise
try(n, i, A, qs) fc
partial solution
on rows 1...i-1
columns to be
tried for row i
fc( )
conflict((i,j), qs)=true
If A = j::B,!
try(n, i, B, qs) fc
either (i,j) is attacked by qs,
!
so try columns from B for row i!
or its safe to extend qs with (i,j); !
if i=n, (i,j)::qs is a full solution;!
otherwise, look for a solution extending (i,j)::qs in row i+1, !
and if this fails, backtrack to try columns from B for row i.
conflict((i,j), qs)=false
try
fun try (n, i, A, qs) fc =!
case A of!
[ ] => fc( )!
| j::B => if conflict((i, j), qs)!
then try (n, i, B, qs) fc!
else if i = n!
then SOME( (i, j)::qs )!
else!
try(n, i+1, upto 1 n, (i, j)::qs)!
(fn ( ) => try (n, i, B, qs) fc)
queens
queens : int -> sol option
REQUIRES: n>0 !
!
ENSURES: !
queens n = SOME qs!
where qs is an n-queens solution,!
if there is one; !
!
queens n = NONE!
otherwise
queens
Look for solution extending [ ]
with a queen in row 1
fun queens n =!
try (n, 1, upto 1 n, nil) !
(fn ( ) => NONE)!
Pessimistic
failure continuation
results
queens 3 = NONE !
!