first commit

This commit is contained in:
Aaron Manning 2021-12-16 21:01:08 +11:00 committed by aaron-jack-manning
commit bba29d0008
20 changed files with 705 additions and 0 deletions

47
README.md Normal file
View File

@ -0,0 +1,47 @@
# OCaml Standard Library
This repository contains my custom OCaml standard library and build system.
## Disclaimer:
This is very bespoke for my requirements, and only something I do/use when I am writing code to be read and used by myself. In general, I do not recommend doing something like this, but this repository is here to show off how one can effectively compile an OCaml project without the standard library but still expose the few functions they may need, and in case my implementations of data structures and algorithms may be useful to people learning functional programming.
## Modules
This library includes the following custom modules:
- List (functional list data structure)
- Queue (functional queue implemented as two lists)
- Set (functional set implemented as a red-black tree)
- Stack (functional stack data structure)
- Tree (functional generic tree type with some general functions to manipulate it)
- Map (functional map implemented as a red-black tree)
## Exposure of Functions from Standard Library
With respect to the exposed parts of the standard library, these are all handled in the `FromStdlib` module, which redefines some definitions directly from the standard library so that this file can be safely included separately, exposing only the desired functions. As such, it is recommended that this file is opened in the code that uses this library, while others are not, and referenced from the module level instead (with one additional exception of `Types`, mentioned in the following section).
All files are compiled with `-nopervasives` except `FromStdlib` (to avoid the headaches in exposing functions like `.printf` which have many dependencies). Linking is also done without `-nopervasives` so that `fromStdlib.cmx` can find the corresponding functions. Hence any new files added to the project are recommended to be compiled separately with `nopervasives` and then linked via the `.cmx` file.
## Type Declarations
In order to prevent duplicate definitions of common types like collections, but still allow things like list literals to work, and to prevent the need of a type annotation at the module level, a `Types` module is provided to be opened in code files which exposes types like `'a list` and other collections.
## Build Process
Since I wanted to compile with some aspects of the Standard library (or import the source files separately), the build process is a little complicated. A makefile is included with the following commands set up:
- `make build` to build all files, including main.
- `make clean` which removes all auto-generated files, leaving only source code behind.
- `make mostlyclean` which remaves all auto-generated files except the main executable, also leaving source code behind.
- `make run` which runs the executable created by `make build`.
- `make install` which runs the equivalent of `make build`, `make mostlyclean` and `make run` in sequence.
Note that incremental builds are not set up, and that some `.mli` files are autogenerated but most are not, and therefore if attempting to edit the standard library code for your own purposes, I recommend running `make clean` first, so you don't attempt to add code to `.mli` files that will be autogenerated and overwritten.
Also take note of the fact that I typically compile everything with `-S` and `-O3` for assembly code files and flambda optimization correspondingly, and this can obviously be changed depending on requirements, as can the use of `ocamlopt` instead of `ocamlc` but if that is changed the final linking will need to be done with `.cmo` instead of `.cmx` files.
## The Core Library
One of the unfortunate consequences of the way OCaml's compilation works, is that there is a library called the core library, documented [here](https://ocaml.org/manual/core.html), which contains some definitions for types and exceptions, yet does not include the code from the stdlib that uses them. When compiling with the `-nopervasives` flag, this is still included but without the standard library. While this makes sense from the perspective of having some fundamental exceptions always available, having types like `list` included makes it very annoying when implemented a custom standard library. This quirk is why my library has no type definition for `list`, `bool`, `option`, etc. but still uses these types.

29
lib/fromStdlib.ml Normal file
View File

@ -0,0 +1,29 @@
(* https://github.com/ocaml/ocaml/blob/cce52acc7c7903e92078e9fe40745e11a1b944f0/stdlib/pervasives.ml *)
let failwith = Stdlib.failwith
let printf = Printf.printf
external ( |> ) : 'a -> ('a -> 'b) -> 'b = "%revapply"
external ( + ) : int -> int -> int = "%addint"
external ( - ) : int -> int -> int = "%subint"
external ( * ) : int -> int -> int = "%mulint"
external ( / ) : int -> int -> int = "%divint"
external ( mod ) : int -> int -> int = "%modint"
external ( +. ) : float -> float -> float = "%addfloat"
external ( -. ) : float -> float -> float = "%subfloat"
external ( *. ) : float -> float -> float = "%mulfloat"
external ( /. ) : float -> float -> float = "%divfloat"
external ( = ) : 'a -> 'a -> bool = "%equal"
external ( <> ) : 'a -> 'a -> bool = "%notequal"
external ( < ) : 'a -> 'a -> bool = "%lessthan"
external ( > ) : 'a -> 'a -> bool = "%greaterthan"
external ( <= ) : 'a -> 'a -> bool = "%lessequal"
external ( >= ) : 'a -> 'a -> bool = "%greaterequal"
external not : bool -> bool = "%boolnot"
external ( or ) : bool -> bool -> bool = "%sequor"
external ( & ) : bool -> bool -> bool = "%sequand"

5
lib/functions.ml Normal file
View File

@ -0,0 +1,5 @@
let id (x : 'a) = x
let ( >> ) f g x = g (f x)
let ( << ) g f x = g (f x)

8
lib/functions.mli Normal file
View File

@ -0,0 +1,8 @@
(** Identity function. *)
val id : 'a -> 'a
(** Function composition. (f >> g) x represents g (f x). *)
val ( >> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
(** Function composition. (f << g) x represents f (g x). *)
val ( << ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

146
lib/list.ml Normal file
View File

@ -0,0 +1,146 @@
open FromStdlib
open Types
let empty : 'a list = []
let head (ls : 'a list) : 'a option =
match ls with
| x :: xs -> Some x
| [] -> None
let tail (ls : 'a list) : 'a list =
match ls with
| x :: xs -> xs
| [] -> []
let rec reverse_helper (ls : 'a list) (acc : 'a list) : 'a list =
match ls with
| x :: xs -> reverse_helper xs (x :: acc)
| [] -> acc
let reverse (ls : 'a list) : 'a list =
reverse_helper ls []
let rec append_tr_helper (ls1_rev : 'a list) (ls2 : 'a list) : 'a list =
match ls1_rev with
| [] -> ls2
| x :: xs ->
append_tr_helper xs (x :: ls2)
let append_tr (ls1 : 'a list) (ls2 : 'a list) : 'a list =
append_tr_helper (reverse ls1) ls2
let ( @ ) = append_tr
let rec map (f : 'a -> 'b) (ls : 'a list) : 'b list =
match ls with
| x :: xs -> f x :: map f xs
| [] -> []
let rec map_rev_tr_helper (f : 'a -> 'b) (ls : 'a list) (acc : 'b list) : 'b list =
match ls with
| x :: xs -> map_rev_tr_helper f xs (f x :: acc)
| [] -> acc
let map_rev_tr (f : 'a -> 'b) (ls : 'a list) : 'b list =
map_rev_tr_helper f ls []
let rec map_tr_helper (f : 'a -> 'b) (ls : 'a list) (acc : 'b list) : 'b list =
match ls with
| x :: xs -> map_tr_helper f xs (f x :: acc)
| [] -> reverse acc
let rec map_tr (f : 'a -> 'b) (ls : 'a list) : 'b list =
map_tr_helper f ls []
let rec foldl (f : 's -> 'a -> 's) (init : 's) (ls : 'a list) : 's =
match ls with
| x :: xs -> xs |> foldl f (f init x)
| [] -> init
let rec foldr (f : 's -> 'a -> 's) (init : 's) (ls : 'a list) : 's =
foldl f init (reverse ls)
let rec zip2_rev_tr_helper (ls1 : 'a list) (ls2 : 'b list) (acc : ('a * 'b) list) : ('a * 'b) list result =
let first_head = head ls1 in
let second_head = head ls2 in
if first_head = None & second_head = None then
Success acc
else if (first_head = None & second_head <> None) or (first_head <> None & second_head = None) then
Error "The two lists must be of the same length."
else
let a = match first_head with | Some x -> x | None -> failwith "if this exception is raised there is an error in the implementation of the function that called it" in
let b = match second_head with | Some x -> x | None -> failwith "if this exception is raised there is an error in the implementation of the function that called it" in
zip2_rev_tr_helper (tail ls1) (tail ls2) ((a, b) :: acc)
let rec zip2_rev_tr (ls1 : 'a list) (ls2 : 'b list) : ('a * 'b) list result =
zip2_rev_tr_helper ls1 ls2 []
let zip2_tr (ls1 : 'a list) (ls2 : 'b list) : ('a * 'b) list result =
match zip2_rev_tr ls1 ls2 with
| Success x -> Success (reverse x)
| Error message -> Error message
let rec unzip2_rev_tr_helper (ls : ('a * 'b) list) (acc1 : 'a list) (acc2 : 'b list) : ('a list) * ('b list) =
match ls with
| (first, second) :: xs -> unzip2_rev_tr_helper xs (first :: acc1) (second :: acc2)
| [] -> (acc1, acc2)
let unzip2_rev_tr (ls : ('a * 'b) list) : ('a list) * ('b list) =
unzip2_rev_tr_helper ls [] []
let rec pairwise_helper (ls : 'a list) (h : 'a) (acc : ('a * 'a) list) : ('a * 'a) list =
match ls with
| a :: (b :: tail) ->
let remaining = b :: tail in
let new_acc = ((a, b) :: acc) in
pairwise_helper remaining h new_acc
| [a] -> (a, h) :: acc
| _ -> acc
let pairwise (ls : 'a list) : ('a * 'a) list =
match ls with
| [] -> []
| [_] -> []
| x :: xs ->
pairwise_helper ls x []
let rec contains (a : 'a) (ls : 'a list) : bool =
match ls with
| x :: xs -> if x = a then true else contains a xs
| [] -> false
let singleton (a : 'a) : 'a list =
a :: []
let rec filter_rev_tr_helper (f : 'a -> bool) (ls : 'a list) (acc : 'a list) : 'a list =
match ls with
| x :: xs ->
if f x then
filter_rev_tr_helper f xs (x :: acc)
else
filter_rev_tr_helper f xs acc
| [] -> acc
let filter_rev_tr (f : 'a -> bool) (ls : 'a list) : 'a list =
filter_rev_tr_helper f ls []
let rec find_helper (f : 'a -> bool) (ls : 'a list) (index : int) : ('a * int) option =
match ls with
| x :: xs -> if f x then Some (x, index) else find_helper f xs (1 + index)
| [] -> None
let find (f : 'a -> bool) (ls : 'a list) : ('a * int) option =
find_helper f ls 0
let rec initialize_helper (f : int -> 'a) (length : int) (index : int) (acc : 'a list) : 'a list =
if length = index then
acc
else
initialize_helper f length (index + 1) ((f index) :: acc)
let initialize (f : int -> 'a) (length : int) : 'a list =
initialize_helper f length 0 []

58
lib/list.mli Normal file
View File

@ -0,0 +1,58 @@
open Types
(** The empty list *)
val empty : 'a list
(** Returns Some [x] if [x] is the head of the list, or None for the empty list. Runs in O(1). *)
val head : 'a list -> 'a option
(** Returns the tail of the list. Will be the empty list if the original list is empty or only contains one element. Runs in O(1). *)
val tail : 'a list -> 'a list
(** Reverses the order of the elements in the list. [reverse] is tail recursive. Runs in O(n). *)
val reverse : 'a list -> 'a list
(** Appends two lists together, by reversing the first and then appending each element from the first to the second. Runs in O(n), where n is the length of the first list. *)
val ( @ ) : 'a list -> 'a list -> 'a list
(** Maps the given function to each element of the list. [map] is not tail recursive and preserves order. Runs in O(n). *)
val map : ('a -> 'b) -> 'a list -> 'b list
(** Maps the given function to each element of the list. [map_rev_tr] is tail recursive and reverses the order of the list. If you wish to preserve order consider [map] for small lists, and [map_tr] for longer lists. Runs in O(n). *)
val map_rev_tr : ('a -> 'b) -> 'a list -> 'b list
(** Maps the given function to each element of the list. [map_tr] is tail recursive, and therefore has to reverse the resulting list. Calling [map_tr] is equivalent to calling [reverse] on the result of [map_rev_tr]. Runs in O(n). *)
val map_tr : ('a -> 'b) -> 'a list -> 'b list
(** Folds the list from the left, applying [f] to the existing state and each element at each step, using [init] as the initial state. [foldl] is tail recursive. Runs in O(n). *)
val foldl : ('s -> 'a -> 's) -> 's -> 'a list -> 's
(** Folds the list from the right, by reversing the list and then calling [foldl]. Runs in O(n). *)
val foldr : ('s -> 'a -> 's) -> 's -> 'a list -> 's
(** Combines two lists into a list of tuples, pairing elements in order and returning a list of pairs in reverse order compared to the original lists. [zip2_rev_tr] is tail recursive. Returns result type to handle errors where the two lists are not equal in length. Runs in O(n). *)
val zip2_rev_tr : 'a list -> 'b list -> ('a * 'b) list result
(** Combines two lists into a list of tuples, pairing elements in order from the front. [zip2_rev_tr] is tail recursive. Returns result type to handle errors where the two lists are not equal in length. This is equivalent to reversing the result of [zip2_rev_tr]. Runs in O(n). *)
val zip2_tr : 'a list -> 'b list -> ('a * 'b) list result
(** Converts a list of tuples into a tuple of lists, reversing the order of the elements in the resulting two lists. Runs in O(n). *)
val unzip2_rev_tr : ('a * 'b) list -> 'a list * 'b list
(* Returns all adjascent pairs of values from the list, as well as a pair of the last element and the first element. Runs in O(n). *)
val pairwise : 'a list -> ('a * 'a) list
(** Determines if the specified element is contained within the list, and returns a boolean. Runs in O(n). If this function is being called many times, consider a Set. *)
val contains : 'a -> 'a list -> bool
(** Creates a list of a single element. Runs in O(1). *)
val singleton : 'a -> 'a list
(** Filters a list according to the provided function, including elements where the function evaluates to true. The list is reversed in the process, and [filter_rev_tr] is tail recursive. Runs in O(n). *)
val filter_rev_tr : ('a -> bool) -> 'a list -> 'a list
(** Finds the first element in the provided list where the given function evaluates to true. Returns Some of the element and the index it appeared at, or none if the given function is false for all elements. Runs in O(n). *)
val find : ('a -> bool) -> 'a list -> ('a * int) option
(** Initializes a list using a length and a function from the index to the desired list value. Runs in O(n). *)
val initialize : (int -> 'a) -> int -> 'a list

4
lib/main.ml Normal file
View File

@ -0,0 +1,4 @@
open FromStdlib open Types
let _ = printf "Hello, World\n"

43
lib/makefile Normal file
View File

@ -0,0 +1,43 @@
# build recompiles and links all files (no incemental builds)
build:
# fromStdlib manages things that need to be exposed from the standard library
ocamlopt -i fromStdlib.ml > fromStdlib.mli
ocamlopt -S -O3 -c fromStdlib.mli fromStdlib.ml
# types for other modules separated to easily expose without module reference in projects
ocamlopt -S -O3 -nopervasives -c types.mli types.ml
# the following files make up the core custom standard library code
ocamlopt -S -O3 -nopervasives -c functions.mli functions.ml
ocamlopt -S -O3 -nopervasives -c stack.mli stack.ml
ocamlopt -S -O3 -nopervasives -c list.mli list.ml
ocamlopt -S -O3 -nopervasives -c map.mli map.ml
ocamlopt -S -O3 -nopervasives -c queue.mli queue.ml
ocamlopt -S -O3 -nopervasives -c set.mli set.ml
ocamlopt -S -O3 -nopervasives -c tree.mli tree.ml
# main is the main file to run code in
ocamlopt -i main.ml > main.mli
ocamlopt -S -O3 -nopervasives -c main.mli main.ml
# after all files are individually compiled with -nopervasives, this is compiled with it so that fromStdlib has the necessary linking
ocamlopt -S -O3 fromStdlib.cmx types.cmx functions.cmx stack.cmx list.cmx map.cmx queue.cmx set.cmx tree.cmx main.cmx -o program
# clean removes all except source files. autogenerated mli files are also removed.
clean:
rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma fromStdlib.mli main.mli program
# mostlyclean removes all except source files and the final executable. autogenerated mli files are also removed.
mostlyclean:
rm -f *.o *.a *.s *.cmi *.cmx *.cmxa *.cmo *.cma fromStdlib.mli main.mli
# install builds, mostlycleans and runs
install:
make build
make mostlyclean
./program
# just runs the executable if it has been produced by make build
make run:
./program

68
lib/map.ml Normal file
View File

@ -0,0 +1,68 @@
open FromStdlib
module type Map = sig
type key
type 'v map
val empty : 'v map
val insert : (key * 'v) -> 'v map -> 'v map
val fetch : key -> 'v map -> 'v option
end
module type MapSpecification = sig
type key
val comparison : key -> key -> int
end
module RBTreeMap (M : MapSpecification) : Map with type key = M.key = struct
type colour =
| Red
| Black
type key = M.key
type 'v map =
| Leaf
| Branch of colour * (key * 'v) * 'v map * 'v map
let empty = Leaf
let rec fetch k = function
| Leaf -> None
| Branch (_, (key, value), left, right) ->
if M.comparison k key < 0 then
fetch k left
else if M.comparison k key > 0 then
fetch k right
else
Some value
let balance = function
| Black, z, Branch (Red, y, Branch (Red, x, a, b), c), d
| Black, z, Branch (Red, x, a, Branch (Red, y, b, c)), d
| Black, x, a, Branch (Red, z, Branch (Red, y, b, c), d)
| Black, x, a, Branch (Red, y, b, Branch (Red, z, c, d)) ->
Branch (Red, y, Branch (Black, x, a, b), Branch (Black, z, c, d))
| a, b, c, d -> Branch (a, b, c, d)
let rec insert_helper (k, v) = function
| Leaf -> Branch (Red, (k, v), Leaf, Leaf)
| Branch (colour, (key, value), left, right) ->
if M.comparison k key < 0 then
balance (colour, (key, value), insert_helper (k, v) left, right)
else if M.comparison k key > 0 then
balance (colour, (key, value), left, insert_helper (k, v) right)
else
Branch (colour, (key, v), left, right)
let insert ((k, v) : (key * 'v)) map =
match insert_helper (k, v) map with
| Branch (_, (key, value), left, right) -> Branch (Black, (key, value), left, right)
| Leaf -> failwith "if this exception is raised there is an error in the implementation of the function that called it"
end

25
lib/map.mli Normal file
View File

@ -0,0 +1,25 @@
module type Map = sig
(** The type of keys in the map. *)
type key
(** Purely functional map, parameterised by the values stored in the map (not the keys). *)
type 'v map
(** The empty map. *)
val empty : 'v map
(** Inserts the key-value pair into the map. If the key already exists, it is overwritten. Runs in O(log(n)). *)
val insert : key * 'v -> 'v map -> 'v map
(** Fetches a value from the map based on the key. Returns an option to handle cases where the key is not present. Runs in O(log(n)). *)
val fetch : key -> 'v map -> 'v option
end
module type MapSpecification = sig
(** The type of keys in the map. *)
type key
(** Comparison should return a value less than 0 for when the first value is smaller, greater than 0 for when the second is smaller, and 0 when they are equivalent. *)
val comparison : key -> key -> int
end
module RBTreeMap (M : MapSpecification) : Map with type key = M.key

13
lib/queue.ml Normal file
View File

@ -0,0 +1,13 @@
open List
open Types
let enqueue (a : 'a) (qu : 'a queue) : 'a queue =
{ qu with front = a :: qu.back }
let dequeue (qu : 'a queue) : ('a option * 'a queue) =
match qu.front with
| [a] -> (Some a, { front = List.reverse qu.back; back = [] })
| h :: t -> (Some h, { qu with front = t })
| [] -> (None, qu)
let empty = { front = []; back = [] }

10
lib/queue.mli Normal file
View File

@ -0,0 +1,10 @@
open Types
(** Adds an element to the back of the queue, returning the new queue. Runs in O(1). *)
val enqueue : 'a -> 'a queue -> 'a queue
(** Removes an element from the front of the queue, returning a tuple of the new queue and an option which is None if the queue is empty, or Some [x] where [x] wa the element on the front of the queue. Runs in best case O(1), worse case O(n) and amortized O(1). *)
val dequeue : 'a queue -> 'a option * 'a queue
(** The empty queue. *)
val empty : 'a queue

121
lib/set.ml Normal file
View File

@ -0,0 +1,121 @@
open FromStdlib
module type RBTreeSet = sig
type member
type set
val empty : set
val insert : member -> set -> set
val member : member -> set -> bool
val union : set -> set -> set
val of_list : member list -> set
val remove : member -> set -> set
val difference : set -> set -> set
val cardinality : set -> int
end
module type SetSpecification = sig
type member
val comparison : member -> member -> int
end
module RBTreeSet (M : SetSpecification) : RBTreeSet with type member = M.member = struct
type colour =
| Red
| Black
type member = M.member
type set =
| Leaf
| Branch of colour * member * set * set
let empty = Leaf
let rec member v = function
| Leaf -> false
| Branch (_, value, left, right) ->
if M.comparison v value < 0 then
member v left
else if M.comparison v value > 0 then
member v right
else
true
let balance = function
| Black, z, Branch (Red, y, Branch (Red, x, a, b), c), d
| Black, z, Branch (Red, x, a, Branch (Red, y, b, c)), d
| Black, x, a, Branch (Red, z, Branch (Red, y, b, c), d)
| Black, x, a, Branch (Red, y, b, Branch (Red, z, c, d)) ->
Branch (Red, y, Branch (Black, x, a, b), Branch (Black, z, c, d))
| a, b, c, d -> Branch (a, b, c, d)
let rec insert_helper v = function
| Leaf -> Branch (Red, v, Leaf, Leaf)
| Branch (colour, value, left, right) ->
if M.comparison v value < 0 then
balance (colour, value, insert_helper v left, right)
else if M.comparison v value > 0 then
balance (colour, value, left, insert_helper v right)
else
Branch (colour, v, left, right)
let insert (v : member) (set : set) : set =
match insert_helper v set with
| Branch (_, value, left, right) -> Branch (Black, value, left, right)
| Leaf -> failwith "if this exception is raised there is an error in the implementation of the function that called it"
let rec union set1 set2 =
match set1 with
| Leaf -> set2
| Branch (colour, value, left, right) ->
union left (union right (insert value set2))
let rec of_list_helper (ls : member list) (acc : set) : set =
match ls with
| x :: xs -> of_list_helper xs (insert x acc)
| [] -> acc
let of_list ls = of_list_helper ls empty
let rec remove (v : member) (set : set) : set =
match set with
| Leaf -> Leaf
| Branch (colour, value, left, right) ->
if M.comparison v value < 0 then
balance (colour, value, remove v left, right)
else if M.comparison v value > 0 then
balance (colour, value, left, remove v right)
else
union left right
(* This needs a more efficient implementation. *)
let rec difference (set1 : set) (set2 : set) : set =
match set2 with
| Leaf -> set1
| Branch (_, value, left, right) ->
difference (union left right) (remove value set1)
(* Need a tail recursive implementation. *)
let rec cardinality (set : set) : int =
match set with
| Leaf -> 0
| Branch (_, _, left, right) ->
cardinality left + cardinality right + 1
end
(* Planned Functions:
- to_list
- intersection
- equal
*)

41
lib/set.mli Normal file
View File

@ -0,0 +1,41 @@
module type RBTreeSet = sig
(** Type of members in the set. *)
type member
(** Purely functional set, implemented as a red, black tree. *)
type set
(** The empty set. *)
val empty : set
(** Inserts a new element into the set. Runs in O(log(n)). *)
val insert : member -> set -> set
(** Determines if a specific value is a member of the set. Runs in O(log(n)). *)
val member : member -> set -> bool
(** Computes the union of two sets. Runs in O(mlog(n)), for the first set is of length m and the second is of length n. Hence if the lengths of the sets are known, this algorithm is most efficient if the second set is the longer one. *)
val union : set -> set -> set
(** Creates a set from a list. Will deduplicate if necessary. Runs in O(log(n!)). *)
val of_list : member list -> set
(** Removes the element specified from the set, if it exists. *)
val remove : member -> set -> set
(** Calculates the first set, without any elements that appear in the second. *)
val difference : set -> set -> set
(** Calculates the number of elements in the set. Note this is not tail recursive. Runs in O(n). *)
val cardinality : set -> int
end
module type SetSpecification = sig
(** Type of members in the set. *)
type member
(** Comparison should return a value less than 0 for when the first value is smaller, greater than 0 for when the second is smaller, and 0 when they are equivalent. *)
val comparison : member -> member -> int
end
module RBTreeSet (M : SetSpecification) : RBTreeSet with type member = M.member

30
lib/stack.ml Normal file
View File

@ -0,0 +1,30 @@
open FromStdlib
open Types
let pop (st : 'a stack) : 'a option * 'a stack =
match st with
| Empty -> (None, Empty)
| Stacked (x, xs) -> (Some x, xs)
let push (a : 'a) (st : 'a stack) : 'a stack =
Stacked (a, st)
let peek (st : 'a stack) : 'a option =
match st with
| Empty -> None
| Stacked (x, xs) -> Some x
let empty : 'a stack = Empty
let rec height_helper (height : int) (st : 'a stack) =
match st with
| Empty -> height
| Stacked (x, xs) -> height_helper (height + 1) xs
let height (st : 'a stack) : int =
height_helper 0 st
let is_empty (st : 'a stack) =
match st with
| Empty -> true
| Stacked _ -> false

19
lib/stack.mli Normal file
View File

@ -0,0 +1,19 @@
open Types
(** Removes the top element from the stack, returning a tuple of the new stack, and None if the stack was empty, or Some [x] if [x] was on top of the stack. Popping an empty stack will result in the returned stack also being empty. Runs in O(1). *)
val pop : 'a stack -> 'a option * 'a stack
(** Pushes the specified element to the top of the stack, returning the new stack. Runs in O(1). *)
val push : 'a -> 'a stack -> 'a stack
(** Examines the top element of the stack, returning None for an empty stack, or Some [x] if [x] is the top element of the stack. Runs in O(1). *)
val peek : 'a stack -> 'a option
(** The empty stack. *)
val empty : 'a stack
(** Finds the height of the stack. Runs in O(n). *)
val height : 'a stack -> int
(** Determines if the stack is empty. Runs in O(1). *)
val is_empty : 'a stack -> bool

5
lib/tree.ml Normal file
View File

@ -0,0 +1,5 @@
open List
open Types
let combine (tr1 : 'a tree) (tr2 : 'a tree) (topBranch : 'a) : 'a tree =
Branch (topBranch, tr1 :: tr2 :: [])

4
lib/tree.mli Normal file
View File

@ -0,0 +1,4 @@
open Types
(* Combines two trees of the same type, with the specified value at the new top node. Runs in O(1). *)
val combine : 'a tree -> 'a tree -> 'a -> 'a tree

13
lib/types.ml Normal file
View File

@ -0,0 +1,13 @@
type 'a queue = { front : 'a list; back : 'a list }
type 'a result =
| Error of string
| Success of 'a
type 'a stack =
| Empty
| Stacked of 'a * 'a stack
type 'a tree =
| Leaf
| Branch of 'a * 'a tree list

16
lib/types.mli Normal file
View File

@ -0,0 +1,16 @@
(** Purely functional queue, implemented as a pair of lists. *)
type 'a queue = { front : 'a list; back : 'a list }
type 'a result =
| Error of string
| Success of 'a
(** Purely functional stack. *)
type 'a stack =
| Empty
| Stacked of 'a * 'a stack
(* A purely functional tree with arbitrarily many branches at each node. *)
type 'a tree =
| Leaf
| Branch of 'a * 'a tree list