CS 334: ML Example Code

Quicksort

(* Always include these at the top of your SML files so 
   large datatype values and lists will print fully *)
Control.Print.printDepth := 100;
Control.Print.printLength := 100;

(* 
   Partition: int * int list -> int list * int list
   Partition list around the pivot, and return the lists
   of smaller and larger elements. 
*)
fun partition (pivot, nil) = (nil, nil)
  | partition (pivot, x::xs) =
      let  
         val (smaller, bigger) = partition (pivot, xs) 
      in
          if x < pivot then (x::smaller, bigger) 
                 else (smaller, x::bigger)
      end;

(* 
   qsort: int list -> int list
   Sort a list of integers using quick sort. 
*)
fun qsort (nil) = nil
  | qsort (p::rest) = 
      let 
         val (smaller, bigger) = partition(p,rest)
      in
          qsort(smaller) @ [p] @ qsort(bigger)
      end;

qsort [6,23,6,3,78,23,12,6,7,34,7,1,23,7,23,6,3,6];

Polymorphic Quicksort

(* Always include these at the top of your SML files so 
   large datatype values and lists will print fully *)
Control.Print.printDepth := 100;
Control.Print.printLength := 100;

(* 
  partition: (('a * 'a -> bool) * 'a * 'a list) 
              -> ('a list * 'a list)
  In this version, you must supply a lessThan fuction for
  comparing values -- note the intended type is above, though
  inference gives you a slightly more general type...
*)
fun partition (lessThan, pivot, nil) = (nil, nil)
  | partition (lessThan, pivot, x::xs) =
      let val (smaller, bigger) = partition(lessThan, pivot, xs) 
      in
    if lessThan(x,pivot) then (x::smaller, bigger) 
                 else (smaller, x::bigger)
      end;

(* qsort: ('a * 'a -> bool) * 'a list -> 'a list *)
fun qsort (lessThan, nil) = nil
  | qsort (lessThan, p::rest) = 
      let val (smaller, bigger) = partition(lessThan, p,rest)
      in
    qsort (lessThan, smaller) @ [p] @ qsort(lessThan, bigger)
      end;


qsort (op<, [6,23,6,3,78,23,12,6,7,34,7]);
qsort (fn (x,y) => (size(x) < size(y)), ["1","4 cows","~12"]);

Equality Types

(*
 * contains: ''a * ''a list -> bool
 * This function illustates equality types.
 *)
fun contains(x, nil) = false
  | contains(x, y::ys) =
      x = y orelse contains(x,ys);

contains(1, [1,2,3]);

contains("cow", ["moo", "moo"]);

contains(2.3, [0.1, 0.2]);  (* BAD -- reals do not admit equality *)

Type Abbreviations

(* 
  Create a simple abbreviation for the tupe type int * int 
*)
type Point = int * int;

val x : Point = (3,4);

Datatypes

Java constants:

public static final int NORTH = 1;
public static final int SOUTH = 2;
public static final int EAST = 3;
public static final int WEST = 4;

public move(int x, int y, int dir) {
  switch (dir) {
    case NORTH: ...
    case ...
  }
}

ML datatype:

(* 
   A datatype declaration that creates a new type
   to represent the compass directions. 
*)
datatype Direction = North | South | East | West;

(* 
   move: (int * int) * Direction -> (int * int)
   A function to move a point in the specified direction
   on the Cartesian plane.
*)
fun move((x,y),North) = (x,y+1) 
  | move((x,y),South) = (x,y-1) 
  | move((x,y),East) = (x+1,y) 
  | move((x,y),West) = (x-1,y);

val dir = West;
move ((1,2), dir);
move ((0,0), North);

Datatype Constructors with Data

(*
   A datatype to represent members of a cow herd.
   Calves keep  (id, weight).
   Milkers keep (name, milk per day, weight).
*)
datatype Cow = Calf of int * int
         | Milker of string * real * int;

(*
   weight : Cow -> int
   Return the weight of a cow in lbs.
*)
fun weight (Calf(id,lbs)) = lbs
  | weight (Milker(name,amt,lbs)) = lbs;

(*
   milk : Cow -> real
   Return the average daily milk production of a cow in gallons.
*)
fun milk (Calf(_,_)) = 0.0
  | milk (Milker(_,amt,_)) = amt;

(*
   milk : Cow list -> real
   Return the average daily milk production for a list of cows.
*)
fun production (nil) = 0.0
  | production (x::xs) = milk(x) + production(xs);

(* Some specific cows *)
val springer = Milker("Springer", 3.4, 1100);
val radish = Calf(1604, 95);
val herd = [springer, radish, Milker("Clementine", 5.1, 750)];

weight(springer);
weight(radish);
milk(springer);
production(herd);
(*
   Another Datatype Example...
   A datatype to represent two different payment schemes.
*)
datatype Payment = Cash of real
                 | Check of string * int * real;

(*
   amount : Payment -> double
   Return the amount of either kind of payment
*)
fun amount (Cash(x)) = x 
  | amount (Check(bank,num,x)) = x;

(*
   isCheck : Payment -> boolean
   Return whether a payment is a check.
*)
fun isCheck (Cash(_)) = false 
  | isCheck (Check(_,_,_)) = true;

(*
   tally : Payment list -> real
   Sum the amounts of all payments in a list
*)
fun tally nil = 0.0
  | tally (p::ps) = amount(p) + tally(ps);


val p1 = Cash(100.00);
val p2 = Check("BankNorth", 1001, 55.55);

amount(p1);
amount(p2);
isCheck(p1);
isCheck(p2);
tally [p1,p2];

Recursive Datatypes

(*
   A recursive datatype to capture simple parse trees
   for expressions.
*)
datatype Expr = Num of int
              | Plus of Expr * Expr
              | Mult of Expr * Expr
              ;

(* 
   eval : Expr -> int
   Compute the value of an expression represented as 
   an Expr.
*)
fun eval(Num(n)) = n 
  | eval(Plus(e1,e2)) = eval(e1) + eval(e2)
  | eval(Mult(e1,e2)) = eval(e1) * eval(e2)
  ;

val e1 = Plus(Num(1), Mult(Num(2),Num(3)));
eval e1;

Polymorphic Recursive Datatypes

(*
   A polymorphic recursive datatype to represent trees
   containing any type of value in the nodes.
*)
datatype 'a Tree = Empty
                 | Node of 'a * 'a Tree * 'a Tree;

(* create an int Tree and a string Tree *)
val iTree = Node(3, Node(1,Empty,Empty), Node(2,Empty,Empty));
val sTree = Node("A", Node("B",Empty,Empty), Node("C",Empty,Empty));

(*
   flatten: 'a Tree -> 'a list
   Return a list of all values stored in a tree.
*)
fun flatten (Empty) = nil 
  | flatten (Node(v,l,r)) = flatten(l) @ [v] @ flatten(r);

flatten iTree;
flatten sTree;