packages feed

alms-0.4.9: examples/ex61-popl-AfArray.alms

(* Example: affine arrays *)

module type AF_ARRAY = sig
  type 'a array : A

  val new : int -> 'a -> 'a array
  val set : 'a array -> int -o 'a -o 'a array
  val get : 'a array -> int -o 'a * 'a array

  val size : 'a array -> int * 'a array
end

#load "libarray"
module A = Array

module AfArray : AF_ARRAY = struct
  type 'a array = 'a A.array

  let new = A.new
  let set (a: 'a array) (ix: int) (v: 'a) =
    A.set a ix v; a
  let get (a: 'a array) (ix: int) =
    (A.get a ix, a)

  let size (a: 'a array) = (A.size a, a)
end

let deposit (a: int AfArray.array) (ix: int) (amount: int) =
  let (balance, a) = AfArray.get a ix in
    AfArray.set a ix (balance + amount)

(*** Some definitions used by the next example. ***)

 (* Placing them here ensures that "make test" will catch if they
  * stop typing, since the example where they are used demonstrates
  * a type error. *)

module A = AfArray

(* Swap the values at the given array indices *)
let swapIndices (a: 'a A.array) (i: int) (j: int) =
  let (ai, a) = A.get a i in
  let (aj, a) = A.get a j in
    A.set (A.set a i aj) j ai

(* Fisher-Yates shuffle *)
let inPlaceShuffle (a: 'a A.array) =
  let rec loop (i: int) (a: 'a A.array) : 'a A.array =
    if i == 0
      then a
      else let j = random_int () % (i + 1) in
             loop (i - 1) (swapIndices a i j) in
  let (n, a) = A.size a in
    loop (n - 1) a

(* Quicksort *)
let inPlaceSort (a: int A.array) =
  let rec quicksort (start: int) (limit: int) (a: int A.array) : int A.array =
    if limit > start
      then let (pivot, a) = A.get a limit in
           let rec loop (i: int) (j: int) (a: int A.array)
                     : int * int A.array =
             if i < limit
               then let (ai, a) = A.get a i in
                      if ai <= pivot
                        then loop (i + 1) (j + 1) (swapIndices a i j)
                        else loop (i + 1) j a
               else (j, a) in
           let (j, a) = loop start start a in
           let a      = swapIndices a j limit in
           let a      = quicksort start (j - 1) a in
             quicksort (j + 1) limit a
      else a in
  let (n, a) = A.size a in
    quicksort 0 (n - 1) a

(* For testing: *)
let listToArray (Cons(x,xs): 'a list) =
  let n = length xs + 1 in
  let rec loop (i: int) (xs: 'a list) (a: 'a A.array) : 'a A.array =
    match xs with
    | Nil        -> a
    | Cons(x,xs) -> loop (i + 1) xs (A.set a i x)
   in loop 1 xs (A.new n x)

let arrayToList (a: 'a A.array) =
  let (n, a) = A.size a in
  let rec loop (i: int) (xs: 'a list) (a: 'a A.array)
            : 'a list * 'a A.array =
        if i < 0
          then (xs, a)
          else let (ai, a) = A.get a i in
                 loop (i - 1) (Cons(ai, xs)) a
   in loop (n - 1) Nil a

module Tests = struct
  let unsorted  = Cons(4,Cons(1,Cons(0,Cons(3,Cons(2,Nil)))))
  let sorted    = Cons(0,Cons(1,Cons(2,Cons(3,Cons(4,Nil)))))
  let sorted'   = fst(arrayToList(inPlaceSort(listToArray(unsorted))))
  let () = if sorted == sorted'
             then ()
             else failwith "test failed: inPlaceSort (1)"

  let sorted' =
        fst(arrayToList(inPlaceSort(inPlaceShuffle(listToArray(sorted)))))
  let () = if sorted == sorted'
             then ()
             else failwith "test failed: inPlaceSort (2)"
end