Anda di halaman 1dari 25

15-150 Fall 2014

Lecture 23

Stephen Brookes

today
lazy functional programming

being lazy
Defer computation until its result is needed

a function value (a thunk)


h : unit -> t
can represent a
delayed computation
for a value of type t

to compute this value,


force the thunk,
by calling h( )

call-by-value
(* repeat : a list -> a list *)!
fun repeat L = L @ repeat L

repeat [0] =>* [0] @ repeat [0]!


=>* [0] @ ([0] @ repeat [0])!
=>* . . . forever

repeat evaluates, to a function,



but repeat L doesnt terminate

lazy lists
a thunk

datatype a lazylist = !
Cons of a * (unit -> a lazylist)

val Cons = fn - : a * (unit -> a lazylist) -> a lazylist

t lazylist is a recursive datatype of (infinite) lazy lists


with elements of type t

A t lazylist value has the form Cons(v, h) where v is a value


of type t and h is a thunk for computing a t lazylist

the head element v is immediately available



force h if you need to use later elements

Zeros
(* zeros : unit -> int lazylist *)!
fun zeros ( ) = Cons(0, zeros)!
!

(* Zeros : int lazylist *)!


val Zeros = zeros( )

Ones
(* ones : unit -> int lazylist *)!
fun ones ( ) = Cons(1, ones)!
!

(* Ones : int lazylist *)!


val Ones = ones( )

Nats
(* nats : int -> (unit -> int lazylist) *)!
fun nats n ( ) = Cons(n, nats (n+1))!
!

(* Nats : int lazylist *)!


val Nats = Cons(0, nats 1)
head tail

Nats is the lazy list

0,1,2,3,4,...

nats 2 ( ) is the lazy list

2,3,4,5,6,...

mklazy
(* mklazy : int list -> int lazylist *)!
!

fun mklazy [ ] = Zeros!


| mklazy (x::L) = Cons(x, fn ( ) => mklazy L)

Converts a finite int list



into an infinite int lazylist

by padding with 0s

representation
A t lazylist value represents a sequence of values of type t

Zeros
Ones
nats 42 ( )
mklazy [1,2,3]

represents 0,0,0,0,

represents 1,1,1,1,

represents 42,43,44,45,

represents 1,2,3,0,0,0,

lazyappend
(* lazyappend : a list * (unit -> a lazylist) -> a lazylist *)!
!

fun lazyappend ([ ], h) = h( )!
| lazyappend (x::xs, h) = !
Cons(x, fn ( ) => lazyappend(xs, h))!
!

lazyappend([0,1], nats 2) =>* ????


lazyappend([0,1], nats 2) =>* Cons(0, h1)

where
h1( ) =>* Cons(1, h2)
and
h2( ) =>* Cons(2, h3)
and so on

lazyappend
(* lazyappend : a list * (unit -> a lazylist) -> a lazylist *)!
!

fun lazyappend ([ ], h) = h( )!
| lazyappend (x::xs, h) = !
Cons(x, fn ( ) => lazyappend(xs, h))!
!

lazyappend([0,1], nats 2) =>* Cons(0, fn ( ) => !


Cons(1, fn ( ) => !
Cons(2, h3)))

where

h3 is (fn ( ) => Cons(3, h4)), etc.

WRONG! Why?

lazyappend
If

h( ) !
represents y0,y1,y2,y3,

then !
lazyappend ([x0,,xk], h)!
represents x0,,xk,y0,y1,y2,y3,

repeat
(* repeat : a list -> a lazylist *)!
(* REQUIRES xs not the empty list *)!
(* ENSURES repeat(xs) terminates!
and represents lazy list consisting of
xs repeated over and over *)!
!

fun repeat xs = lazyappend(xs, fn ( ) => repeat xs)

repeat [0] =>* ???

repeat [0]
repeat [0] =>* Cons(0, h0)
where h0( ) =>* Cons(0, h1))
and h1( ) =>* Cons(0, h2))
and so on
repeat [0] represents 0,0,0,0,0,...

show
(* show : int -> a lazylist -> a list *)!
fun show 0 _ = [ ]!
| show n (Cons(x, h)) = x :: show (n-1) (h( ))

show 5 (repeat [0])



=>* [0,0,0,0,0]

lazy equality
Lazy list values A and B are equal

iff they have the same elements...


For all n 0,

show n A = show n B.

Lazy list expressions are equal if



they both fail to terminate,

or raise the same exception


or they evaluate to equal lazy list values

lazymap
(* lazymap : (a -> b) -> a lazylist -> b lazylist *)!
!

fun lazymap f (Cons(x, h)) = !


Cons(f x, fn ( ) => lazymap f (h( )))

recursive call

is deferred
If f is total and L represents x0,x1,x2,
then lazymap f L represents f(x0),f(x1),f(x2),

lazy/eager map theorem


If f : t1 -> t2 is total,

then for all L : t1 lazylist, and all n 0,
show n (lazymap f L)

= List.map f (show n L)
PROOF?
Why assume that f is total?

lazymap fusion
If f : t

-> t3 and g : t1 -> t2 are total, then


for all L : t1 lazylist
lazymap (f o g) L = lazymap f (lazymap g L)
2

same

as

saying

lazymap (f o g) = (lazymap f) o (lazymap g)

lazyzip
(* lazyzip : a lazylist * b lazylist -> (a * b) lazylist

*)!

fun lazyzip (Cons(x, h), Cons(y, k))!


= Cons((x,y), fn ( ) => lazyzip (h( ), k( )))

recursive call

is deferred
If
A represents a0,a1,a2,!
and B represents b0,b1,b2,!
then lazyzip(A,B) represents (a0,b0),(a1,b1),(a2,b2),!

lazy filter
(* lazyfilter : (a -> bool) -> a lazylist -> a lazylist *)!
!

fun lazyfilter p (Cons(x, h)) =!


if p x then Cons(x, fn ( ) => lazyfilter p (h( )))!
else lazyfilter p (h( ))
keep making recursive calls

until we find an element that satisfies p

after finding an element



that satisfies p,

defer recursive calls

warning
What happens when we evaluate
lazyfilter (fn x => x=0) (repeat [1])

specification
If p is total and L represents a0,a1,a2,!
and infinitely many ai satisfy p
then lazyfilter p L represents the sequence of !
all the ai satisfying p

lazy factorials
(* facts : unit -> int lazy list *)!
!

fun facts( ) = !
Cons(1, fn ( ) => !
lazymap (op * ) (lazyzip(facts( ), !
nats 2 ( )) ) )

facts( ) represents 1!, 2!, 3!, 4!,


1, 2, 6, 24,

Primes
(* sift : int -> int lazylist -> int lazylist *)!
fun sift x = lazyfilter (fn y => y mod x <> 0)!
!

(* sieve : int lazylist -> int lazylist *)!


fun sieve (Cons(x, h)) =!
Cons(x, fn ( ) => sieve (sift x (h( ))))!
!

val Primes = sieve(nats 2 ( ))

Strike out the twos!


Strike out the threes!
...The Sieve of Eratosthenes

sift and sieve


Let L = Cons(x, h) consist of the integers 2
that are not divisible by the first n primes,
in increasing order

prime

Then x is the
And sift x (h( )) consists of the integers 2
th
(n+1)

that are not divisible by the first n+1 primes


So sieve(nats 2 ( )) consists of all the primes

Anda mungkin juga menyukai