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, . . .
eval10
(* eval10 : int list -> real *)!
!
approx10
(* approx10 : int -> int lazylist -> real *)!
!
approx10 n L = !
2
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
carry10
(* carry10 : int lazylist -> int lazylist *)!
approx10 n (carry10 L) !
= approx10 n L
examples
L
carry10 L
norm10
(* norm10 : int lazylist -> int lazylist *)!
!
(* REQUIRES L represents r, !
digits in tail of L between 0 and 18 *)!
!
*)!
examples
L
norm10 L
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
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
In normal form, 0 d
(math!)
limits
(math!)
d0.d1d2...dn...
represents the real number given by
the limit of the sequence
2
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
!
n
decimal digits
We can implement decimal representations
using lazy lists of digits
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
Every real r
has a
normal decimal representation L
factoradic notation
Can represent a real number in factoradic
f
n
f1.f2f3...fn... represents
n=1
n!
In normal form, 0 f
n<
n for n>1
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
factoradic representation
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...
the plan
First, code for manipulating lazy lists of digits
factoradic carry
factoradic normalization
place values
f1.f2f3...fn...
In a factoradic expansion
th
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
y = n * (y div n) + (y mod n)
factoradic
norm
giving the first digit place value n-1
normfac : int -> int lazylist -> int lazylist
!
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 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...
0 fn+1 n
implies
fac2dec
fac2dec : int lazylist -> int lazylist
digits of e
val digits_of_e = !
fac2dec (Cons(2, ones))
show 5 digits_of_e
= [2,7,1,8,2]
list2int
*)
(* 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;
!
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