Standard ML

236319 Spring 2024, Prof. Lorenz


Standard ML

sequences


lazy lists

  • Elements are not evaluated until their values are required
  • May be infinite
  • Example: a sequence of all even integers $0, 2, -2, 4, \ldots$

lazy lists in ML

ML evaluates E in Cons(x,E), so to obtain laziness we must write Cons(x, fn()=>E)

datatype 'a seq = Nil
    | Cons of 'a * (unit -> 'a seq);

fun take s 0 = []
  | take Nil _ = []
  | take (Cons (x, xf)) n = x :: take (xf ()) (n - 1);

examples of sequences

$1, 2, 3, 4, \ldots$

fun from k = Cons (k, fn () => from (k + 1));

from 1;
take it 10;

examples of sequences

$0, 2, -2, 4, \ldots$

fun from_even n = let
    val next = if n > 0 then ~n else ~n + 2
  in
    Cons (n, fn () => from_even next)
  end;

from_even 0;
take it 10;

Processing sequences

squares takes a sequence of integers and returns a sequence of their squares:

fun squares Nil = Nil
  | squares (Cons (x, xf)) =
        Cons (x * x, fn () => squares (xf ()));

squares (from 1);
take it 10;

addq

Implement addq that takes two integer sequences and adds them element-wise

...
(*val addq = fn : int seq * int seq -> int seq*)

addq

fun addq (Cons (x, xf), Cons (y, yf)) =
        Cons (x + y, fn () => addq (xf (), yf ()))
  | addq _ = Nil;
addq (from 1, from 1);
take it 10;

appendq

Implement appendq that appends two sequences

...
(*val appendq = fn : 'a seq * 'a seq -> 'a seq*)

appendq

fun appendq (Nil, yq) = yq
  | appendq (Cons (x, xf), yq) =
        Cons (x, fn () => appendq (xf (), yq));

What would appendq(xq,yq) be if xq is infinite?

mapq

Implement mapq that applies a function on the elements of a sequence

...
(*val mapq = fn : ('a -> 'b) -> 'a seq -> 'b seq*)

mapq

fun mapq f Nil            = Nil
  | mapq f (Cons (x, xf)) =
        Cons (f x, fn () => mapq f (xf ()));
mapq (fn x => x * 3) (from 1);
take it 10;

filterq

Implement filterq that filters a sequence based on a predicate

...
(*val filterq = fn : ('a -> bool) -> 'a seq -> 'a seq*)

filterq

fun filterq pred Nil = Nil
  | filterq pred (Cons (x, xf)) =
        if pred x
        then Cons (x, fn () => filterq pred (xf ()))
        else filterq pred (xf ());
filterq (fn x => x mod 2 <> 0) (from 1);
take it 10;

interleaveq

Implement interleaveq that interleaves two sequences.

e.g.: Interleaving 1,2,3,... and 11,12,13,... returns: 1,11,2,12,3,13,4,...

...
(*val interleaveq = fn : 'a seq * 'a seq -> 'a seq*)

interleaveq

fun interleaveq (Nil, yq)       = yq
  | interleaveq (Cons (x, xf), yq) =
        Cons (x, fn () => interleaveq (yq, xf ()));
interleaveq (from 1, from 11);
take it 10;

dropq

dropq takes a sequence s and a positive number n and returns s without its first n elements

...
(*val dropq = fn: 'a seq -> int -> 'a seq*)

dropq

fun dropq seq 0 = seq
  | dropq Nil _ = Nil
  | dropq (Cons (x, xf)) n = dropq (xf ()) (n - 1);
dropq (from 1) 10;
take it 10;

seqToList

seqToList takes a sequence and returns a list of its elements

...
(*val seqToList = fn: 'a seq -> 'a list*)

seqToList

fun seqToList Nil = []
  | seqToList (Cons (x, xf)) = x :: (seqToList (xf ()));

listToSeq

listToSeq takes a list and returns a sequence of its elements

...
(*val listToSeq = fn: 'a list -> 'a seq*)

listToSeq

fun listToSeq [] = Nil
  | listToSeq (x :: xs) = Cons (x, fn () => listToSeq xs);

Exam Question

q1

...

fraction

fun fraction m n =
  if m mod n = 0
  then Nil
  else Cons (m * 10 div n mod 10, fn () => fraction (m * 10) n);
fraction 1 7;
take it 10;

Exam Question

q2

...

lazy_divide

fun lazy_divide m n = (m div n, fraction (m mod n) n);

Sequences exercise

(a hard one in fact…)

Let’s define a lazy tree datatype, using sequences:

datatype 'a seq = Nil | Cons of 'a * (unit -> 'a seq)

datatype 'a option = NONE | SOME of 'a;

datatype 'a node =
  Node of 'a * (unit -> 'a node option) * (unit -> 'a node option);

type 'a lazy_tree = unit -> 'a node option;

fun take s 0 = []
  | take Nil _ = []
  | take (Cons (x, xf)) n = x :: take (xf ()) (n - 1);

Let’s define some trees:

fun t1 () = NONE;
fun t2 0 () = SOME (Node (0, t1, t1))
  | t2 n () = SOME (Node (n, t2 (n div 2), t2 (n - 1)));

Implement lazy bfs traversal of lazy trees

...
local
  fun aux [] [] = Nil
    | aux [] ts = aux (map (fn t => t ()) ts) []
    | aux (NONE :: ns) ts = aux ns ts
    | aux ((SOME (Node (h, l, r))) :: ns) ts = 
    	Cons(h, fn () => aux ns (ts @ [l, r]))
in
  fun bfs t = aux [t ()] []
end;
take (bfs (t2 10)) 10;