Anda di halaman 1dari 46

15-150 Fall 2014

Lecture 24

Stephen Brookes

background
datatype a lazylist = Cons of a * (unit -> a lazylist)
fun zeros ( ) = Cons(0, zeros);
(* show : int -> a lazylist -> a list *)!
fun show 0 _ = [ ]!
| show n (Cons(x, h)) = x :: show (n-1) (h( ));
(* lazymap : (a -> b) -> a lazylist -> b lazylist *)!
fun lazymap f (Cons(x, h)) = Cons(f x, fn ( ) => lazymap f (h( )));
(* lazyzip : a lazylist * b lazylist -> (a * b) lazylist *)!
fun lazyzip (Cons(x, h), Cons(y, k)) = Cons((x,y), fn ( ) => lazyzip (h( ), k( )));
(* 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( ));

decimal representation
Can represent a real number in decimal
d0.d1d2...dn...
d0 = the integer part
d1 = the tenths part
d2 = the hundredths part
0 dn 9 for n>0
the nth decimal place

n
is worth (1/10)

representation
The lazy list !
d 0, d 1, d 2. . . , d n, . . .

represents the real number!


!

d0 + (1/10)d1 + (1/10)2d2 + + (1/10)ndn +

eval10
(* eval10 : int list -> real *)!
!

fun eval10 [ ] = 0.0!


| eval10 (x::xs) = real x + 0.1 * eval10 xs;
eval10 [d0, d1, d2. . . , dn]
2

= d0 + (1/10)d1 + (1/10) d2 + + (1/10) dn

approx10
(* approx10 : int -> int lazylist -> real *)!
!

fun approx10 n L = eval10 (show (n+1) L)

When L is the lazy list !


d 0, d 1, d 2. . . , d n, . . .

approx10 n L = !
2

d0 + (1/10)d1 + (1/10) d2 + + (1/10) dn

representing reals
Lazy list L represents r if

limn (approx10 n L) = r
fun zeros( ) = Cons(0, zeros);

fun nines( ) = Cons(9, nines);

val X = Cons(1, zeros);

val Y = Cons(0, nines);
X and Y both represent 1.0

decimal validity
A lazy list L is decimal valid iff

all items in the tail of L are between 0 and 9


Every real number has a decimal valid


representation

Representation may not be unique


0, 18, 0, 18, 0, 18, ...
1, 8, 1, 8, 1, 8, ...

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

approx10 n (carry10 L) !
= approx10 n L

(* REQUIRES L represents r, digits in tail 0 *)!


(* ENSURES carry10(L) represents r, !
tenths place is decimal *)!
!

fun carry10 (Cons(x,s)) =!


let!
val (Cons(y,t)) = s( )!
in!
Cons(x + y div 10, fn ( ) => Cons(y mod 10, t))!
end;

examples
L

0, 10, 2, 12, 4, 14, 6, ...

carry10 L

1, 0, 2, 12, 4, 14, 6, ...

carry10 (Cons(1, zeros)) = Cons(1, zeros)



carry10 (Cons(0, nines)) = Cons(0, nines)

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

(* REQUIRES L represents r, !
digits in tail of L between 0 and 18 *)!
!

(* ENSURES If norm10(L) evaluates to N, !


then N represents r, and !
digits in tail of N are between 0 and 9
decimal
!

*)!

fun norm10 (Cons(x, h)) =!


let!
val Cons(y, t) = h( )!
val N = Cons(x, fn ( ) => norm10 (Cons(y, t)))!
in!
if y+1 < 10 then N else carry10 N!
end

examples
L
norm10 L

0, 10, 2, 12, 4, 14, 6, ...


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

norm10 (Cons(1, zeros)) = Cons(1, zeros)



norm10 (Cons(1, nines)) ???

norm10(nines( )) fails to terminate

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

(* REQUIRES !
A represents a, B represents b,!
digits in tail of A & tail of B are decimal

*)!

(* ENSURES !
If add10(A,B) evaluates to L,
then L represents a+b,!
digits in tail of L are decimal *)!

fun add10(A, B) = !
norm10 (lazymap (op +) (lazyzip(A, B)));

lessons
Reasoning about lazy data structures
is not a leisure activity

Need to be careful: prove termination



Can use induction on the number of items

Can use equational reasoning, with the
appropriate lazy notion of equality

Ground rules
e is transcendental, so irrational, !
and its decimal expansion isnt finite or repeating.

No calculation of factorials!

No real arithmetic

No arcane formulae

decimal, schmecimal...
OK, but how about that Google job?

Need to generate the decimal digits of e

decimal notation
Can represent a real number in decimal

d0.d1d2...dn... represents
d0 is the integer part

dn

n=0 10

0.333... represents 1/3

In normal form, 0 d

< 10 for n>0

decimal digits of e are not obvious!

(math!)

limits

(math!)

d0.d1d2...dn...
represents the real number given by
the limit of the sequence
2

d0 + (1/10)d1 + (1/10) d2 + + (1/10) dn

as n
If each di is a decimal digit,

this limit exists!

eval10
eval10 : int list -> real
fun eval10 [ ] = 0.0
| eval10 (x::L) = real x + 0.1* eval10 L

eval10 [d0, d1, d2, . . . , dn] =


2

!
n

d0 + (1/10)d1 + (1/10) d2 + + (1/10) dn

decimal digits
We can implement decimal representations
using lazy lists of digits

L : int lazylist decimal represents the real

number given by the limit of the sequence


eval10 (show n L) as n
(if this limit exists!!!)

decimal representation
L : int lazylist decimal represents r : real iff
for all n0, eval10 (show n L) terminates,
and the limit of this sequence is r

L is a normal decimal iff all digits in the tail of L


are between 0 and 9

Every real r

has a

normal decimal representation L

factoradic notation
Can represent a real number in factoradic

f
n
f1.f2f3...fn... represents

f1 = the integer part

n=1

n!

0.020000... represents 1/3


2.111111... represents e

In normal form, 0 f

n<

n for n>1

factoradic digits of e are obvious!

evalfac
evalfac : int -> int list -> real
fun evalfac i [ ]
= 0.0
| evalfac i (x::L) = real x + (1.0/real i) * evalfac (i+1) L

evalfac 2 [f0, f1, f2, . . . , fn] =

f0 + (1/2!) f1 + (1/3!) f2 + + (1/(n+1)!) fn

factoradic representation

L : int lazylist factoradic represents r : real iff


for all n0, evalfac 2 (show n L) terminates,
and the limit of this sequence of reals is r

L is a normal factoradic iff for all n>1 the nth digit in L


is between 0 and n-1
Every real r

has a

normal factoradic representation L

real number

decimal

factoradic

1/2

0.500...

0.100...

1/3

0.333...

0.0200...

2/3

0.666...

0.1100...

4/5

0.800...

0.113100...

8/5

1.600...

1.102200...

2.7182...

2.111...

notice anything useful?

the plan
First, code for manipulating lazy lists of digits

factoradic carry

factoradic normalization

without affecting the



real value being represented

Then, conversion from factoradic to decimal


producing decimal digits, lazily


normal factoradic and decimal
representations for the same real
have the same integer part

place values
f1.f2f3...fn...
In a factoradic expansion
th

we say that the n digit fn has place value n


and it represents fn/n!

Given a positive integer k and a lazylist L

that represents f0 f1 f2 ...fn ... we see that



(1/k!) * evalfac (k+1) [f0, f1, f2, . . . , fn] =
(1/k!) f0 + (1/(k+1)!) f1 + + (1/(n+1)!) fn

For k=1 this is same as:

evalfac 2 [f0, f1, f2, . . . , fn] =

f0 + (1/2!) f1 + (1/3!) f2 + + (1/(n+1)!) fn

factoradic
carry
giving the first digit place value n-1
(* carryfac : int -> int lazylist -> int lazylist *)!
the carry amount

fun carryfac n (Cons(x, h)) =!
y div n

let!
gets added

to the first digit
val (Cons(y, t)) = h( )!
in!
Cons(x + y div n, fn ( ) => Cons(y mod n, t))!
end
carryfac n

a factoradic rep for r



with x as n-1th digit

a factoradic rep for r,



nth digit between 0 and n-1

y = n * (y div n) + (y mod n)

factoradic
norm
giving the first digit place value n-1
normfac : int -> int lazylist -> int lazylist
!

fun normfac n (Cons(x, h)) =!


does carries,

let!
lazily, as needed
val Cons(y, t) = h( )!
val N = Cons(x, fn ( ) => normfac (n+1) (Cons(y, t)))!
in!
if y+9 < n then N else carryfac n N!
end
assuming max carry is 9,

if y+9 < n then no need to do
carryfac n, because

(y+9) div n = 0

!
x is first normalized digit

if y+9 n we need to do
carryfac n to N, because

(y+9) div n > 0

!
first normalized digit is

x + carry amount

factoradic
norm
with the first digit having place value n-1
!

normfac : int -> int lazylist -> int lazylist

fun normfac n (Cons(x, h)) =!


let!
val Cons(y, t) = h( )!
val N = Cons(x, fn ( ) => normfac (n+1) (Cons(y, t)))!
in!
if y+9 < n then N else carryfac n N!
end (* assuming max carry is 9, ensures next digit is < n *)
a factoradic rep for r,

with bounded digits

normfac 2

max carry is 9
needs to be

justified when using normfac

a normal

factoradic rep for r

conversion
Given a normal factoradic L for r

Compute the decimal digits of r, lazily
given f0.f1...fn... compute d0.d1...dn...
given 2.1111... compute 2.7182...

conversion
r = f0.f1...fn...

r = d0.d1...dn...

Normal factoradic and decimal reps for r


have the same integer part, so d0 = f0

shift and multiply factoradic digits by 10 to get


10*(r-f0) = 0.(10*f1)...(10*fn)... [not normal!]

Use normfac 2 to get a normal factoradic for 10*(r-f )



The integer part in this factoradic will be d of r,
0

because 10*(r-f0) = 10*(r-d0) = d1.d2...dn...

And so on, as needed, to generate d , ... d , ...


2

shift and multiply


shiftmult : int lazylist -> int lazylist
fun shiftmult L = !
Cons(0, fn ( ) => lazymap (fn x => 10*x) L)
When L is normal,

the maximum carry value to nth place

when normalizing shiftmult L is 9

0 fn+1 n
implies

0 (10 * fn+1)div (n+1) 9

fac2dec
fac2dec : int lazylist -> int lazylist

fun fac2dec (Cons(x, s)) =!


Cons(x, fn ( ) => !
fac2dec(normfac 2 (shiftmult(s( )))))
(* REQUIRES L = normal factoradic for r
*)
(* ENSURES fac2dec(L) = normal decimal for r *)
(modulo termination)

digits of e
val digits_of_e = !
fac2dec (Cons(2, ones))
show 5 digits_of_e

= [2,7,1,8,2]

list2int

list2int : int list -> int

(* REQUIRES L is a list of decimal digits

*)

(* ENSURES

list2int(L) = the integer represented by L *)
list2int [2,7,1,8,2] = 27182

isprime
isprime : int -> bool
(* REQUIRES n>0

*)

(* ENSURES

isprime(n) = true if n is prime

isprime(n) = false if n is not prime *)

collate
collate : int -> int lazylist -> (int list) lazylist
fun collate n (L as Cons(_, h)) =!
Cons(show n L, fn ( ) => collate n (h( )))

google
google : int -> int lazylist
fun google n =!
lazyfilter isprime !
(lazymap list2int !
(collate n (digits_of_e)))

results
- google 7;!
val it = Cons (2718281,fn) : int lazylist!

FAST!

- google 8;!
val it = Cons (72407663,fn) : int lazylist!

SLOW!

Unfortunately...
- google 10;
!

uncaught exception Overflow



10-digit integers are too big!

solution
Use ML library module

IntInf

for infinite precision
integer arithmetic
open IntInf;
opening IntInf

type int = int

...

val ~ : int -> int

val + : int * int -> int

val * : int * int -> int

val div : int * int -> int

val mod : int * int -> int

...

val > : int * int -> bool

...

val compare : int * int -> order

(then re-compile...)

results
!

- google 9;!
val it = Cons (360287471,fn) : int lazylist!
!

- google 10;!
val it = Cons (7427466391,fn) : int lazylist!
!

- google 11;!
val it = Cons (75724709369,fn) : int lazylist!

results
- show 5 (google 10);!
val it = [7427466391,!
7413596629,!
6059563073,!
3490763233,!
2988075319] : int list
(7427466391 begins at the 99th digit of e)

summary
Yes, we can

compute the decimal digits of e



without use of factorials
Exercise
Given two normal factoradic reps, compute their sum

Anda mungkin juga menyukai