|
| 1 | +open Printf |
| 2 | + |
| 3 | +(*****************************************************************************) |
| 4 | +(* *) |
| 5 | +(* Set Data Structure *) |
| 6 | +(* *) |
| 7 | +(* Sets are a {e finite collection} of {b unique} data with roots in *) |
| 8 | +(* mathematics. The way sets are implemented allow for logarithmic-time *) |
| 9 | +(* access at the cost of slower write times than an array. Sets don't *) |
| 10 | +(* guarantee element order and they can't be repeated, they must be unique *) |
| 11 | +(* and depending on the language, of same type (mostly). *) |
| 12 | +(* OCaml doesn't let you have sets of different types and the standard *) |
| 13 | +(* library implements them as functors, meaning, you need to pass a module *) |
| 14 | +(* to the [Set.Make] functor in order to get back a new module that is *) |
| 15 | +(* bound to the type that the passed module represent. Among the most *) |
| 16 | +(* popular modules to pass are [String], [Float], [Int], and [Char]. *) |
| 17 | +(* *) |
| 18 | +(* Set operations are: *) |
| 19 | +(* - Union (A ∪ B): A set composed of all the elements from sets A and B. *) |
| 20 | +(* - Intersection (A ∩ B): Elements of A and B that appear on both sets. *) |
| 21 | +(* - Symmetric difference (A Δ B): All the elements of A and B that don't *) |
| 22 | +(* appear on both at the same time. Ex: {1,2,3} Δ {4,2,6} = {1,3,4,6}. *) |
| 23 | +(* - Difference (A - B): The elements of A without the ones that also *) |
| 24 | +(* appear on B; basically, subtration. Ex: {9,1,4,7} - {1,7,5,2} = {9,4}. *) |
| 25 | +(* *) |
| 26 | +(*****************************************************************************) |
| 27 | + |
| 28 | +module IntSet = Set.Make (Int) |
| 29 | + |
| 30 | +let show_int_set s = |
| 31 | + if IntSet.is_empty s |
| 32 | + then "∅" |
| 33 | + else |
| 34 | + IntSet.to_list s |
| 35 | + |> List.map string_of_int |
| 36 | + |> String.concat ", " |
| 37 | + |> sprintf "{ %s }" |
| 38 | +;; |
| 39 | + |
| 40 | +let tap str set = |
| 41 | + printf str (show_int_set set); |
| 42 | + set |
| 43 | +;; |
| 44 | + |
| 45 | +(** [symm_diff a b] is the symmetric difference between set [a] and set [b]. |
| 46 | + A symmetric difference can be described as the difference between the |
| 47 | + union of [a] and [b], and their intersection. *) |
| 48 | +let symm_diff a b = IntSet.diff (IntSet.union a b) (IntSet.inter a b) |
| 49 | + |
| 50 | +let _ = |
| 51 | + let b_set = IntSet.of_list [ 5; 10; 6; 0 ] in |
| 52 | + let a_set = |
| 53 | + IntSet.empty |
| 54 | + |> tap "Empty set: %s\n" |
| 55 | + |> IntSet.add 8 |
| 56 | + |> tap "After adding 1 element: %s\n" |
| 57 | + |> IntSet.add_seq (List.to_seq [ 1; 5; 10 ]) |
| 58 | + (* NOTE: Adding elements to a certain position is irrelevant in an OCaml set |
| 59 | + as they can't guarantee insertion order since the elements get sorted. *) |
| 60 | + |> tap "After adding multiple elements: %s\n" |
| 61 | + |> IntSet.remove 8 |
| 62 | + |> tap "After removing the number 8: %s\n" |
| 63 | + in |
| 64 | + printf |
| 65 | + "Union: A (%s) ∪ B (%s) = %s\n" |
| 66 | + (show_int_set a_set) |
| 67 | + (show_int_set b_set) |
| 68 | + (IntSet.union a_set b_set |> show_int_set); |
| 69 | + printf |
| 70 | + "Intersection: A (%s) ∩ B (%s) = %s\n" |
| 71 | + (show_int_set a_set) |
| 72 | + (show_int_set b_set) |
| 73 | + (IntSet.inter a_set b_set |> show_int_set); |
| 74 | + printf |
| 75 | + "Difference: B (%s) - A (%s) = %s\n" |
| 76 | + (show_int_set b_set) |
| 77 | + (show_int_set a_set) |
| 78 | + (IntSet.diff b_set a_set |> show_int_set); |
| 79 | + printf |
| 80 | + "Symmetric Difference: B (%s) Δ A (%s) = %s\n" |
| 81 | + (show_int_set a_set) |
| 82 | + (show_int_set b_set) |
| 83 | + (symm_diff a_set b_set |> show_int_set); |
| 84 | + printf |
| 85 | + "Is 0 an element of set B = %s? %b\n" |
| 86 | + (show_int_set b_set) |
| 87 | + (IntSet.mem 0 b_set); |
| 88 | + printf "Cardinality (elt. count) of set A: %d\n" (IntSet.cardinal a_set) |
| 89 | +;; |
| 90 | + |
| 91 | +(* Output of [dune exec reto18] |
| 92 | +
|
| 93 | + Empty set: ∅ |
| 94 | + After adding 1 element: { 8 } |
| 95 | + After adding multiple elements: { 1, 5, 8, 10 } |
| 96 | + After removing the number 8: { 1, 5, 10 } |
| 97 | + Union: A ({ 1, 5, 10 }) ∪ B ({ 0, 5, 6, 10 }) = { 0, 1, 5, 6, 10 } |
| 98 | + Intersection: A ({ 1, 5, 10 }) ∩ B ({ 0, 5, 6, 10 }) = { 5, 10 } |
| 99 | + Difference: B ({ 0, 5, 6, 10 }) - A ({ 1, 5, 10 }) = { 0, 6 } |
| 100 | + Symmetric Difference: B ({ 1, 5, 10 }) Δ A ({ 0, 5, 6, 10 }) = { 0, 1, 6 } |
| 101 | + Is 0 an element of set B = { 0, 5, 6, 10 }? true |
| 102 | + Cardinality (elt. count) of set A: 3 |
| 103 | +*) |
| 104 | + |
| 105 | +(*****************************************************************************) |
| 106 | +(* *) |
| 107 | +(* Implementation and Ordering *) |
| 108 | +(* *) |
| 109 | +(* Sets can be implemented in 2 ways: with a tree-like data structure and *) |
| 110 | +(* using a hash table. A binary properly balanced binary tree can give us *) |
| 111 | +(* logarithmic access times and the traversal is done in orderly manner. In *) |
| 112 | +(* OCaml's standard lib, [Set] is implemented with a binary tree and that's *) |
| 113 | +(* why the module we pass to the functor must have a comparator function *) |
| 114 | +(* called [compare]. If we were to implement a set that remembers insertion *) |
| 115 | +(* order we could add both to a linked list and a b-tree or hash bucket. *) |
| 116 | +(* It's worth noting that if we wanted a data structure to add stuff to *) |
| 117 | +(* either end (front/rear) or at a specific position then maybe we're better *) |
| 118 | +(* off with an array or a linked list which can be converted to a set at a *) |
| 119 | +(* later time anyway (getting rid of duplicates). *) |
| 120 | +(* *) |
| 121 | +(*****************************************************************************) |
0 commit comments