You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Import Rewrote Set Tutorial from V2 PR
@NebuPookins rewrote the set tutorial for [V2](https://github.com/ocaml/v2.ocaml.org) in 2021. The PR was neither merged nor rejected:
ocaml/v2.ocaml.org#1596
* Apply suggestions from code review
Co-authored-by: Christine Rose <christinerose@users.noreply.github.com>
* Minor Fixes
As pointed by @Octachron
* line editing
* shorten a lot and present common operations instead of explaining functors
* Review edits
---------
Co-authored-by: Cuihtlauac ALVARADO <cuihtmlauac@tarides.com>
Co-authored-by: Christine Rose <christinerose@users.noreply.github.com>
Co-authored-by: Sabine Schmaltz <sabineschmaltz@gmail.com>
`Set` provides the functor `Set.Make`. You must start by passing `Set.Make` a module. It specifies the element type for your set. In return, you get another module with those elements' set operations.
14
+
15
+
If you need to work with string sets, you must invoke `Set.Make(String)`. That returns a new module.
14
16
```ocaml
15
-
# module SS = Set.Make(String);;
16
-
module SS :
17
+
# module StringSet = Set.Make(String);;
18
+
module StringSet :
17
19
sig
18
20
type elt = string
19
21
type t = Set.Make(String).t
20
22
val empty : t
21
-
val is_empty : t -> bool
22
-
val mem : elt -> t -> bool
23
23
val add : elt -> t -> t
24
24
val singleton : elt -> t
25
25
val remove : elt -> t -> t
26
26
val union : t -> t -> t
27
27
val inter : t -> t -> t
28
-
val disjoint : t -> t -> bool
29
-
val diff : t -> t -> t
30
-
val compare : t -> t -> int
31
-
val equal : t -> t -> bool
32
-
val subset : t -> t -> bool
33
-
val iter : (elt -> unit) -> t -> unit
34
-
val map : (elt -> elt) -> t -> t
35
-
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
36
-
val for_all : (elt -> bool) -> t -> bool
37
-
val exists : (elt -> bool) -> t -> bool
38
-
val filter : (elt -> bool) -> t -> t
39
-
val filter_map : (elt -> elt option) -> t -> t
40
-
val partition : (elt -> bool) -> t -> t * t
41
-
val cardinal : t -> int
42
-
val elements : t -> elt list
43
-
val min_elt : t -> elt
44
-
val min_elt_opt : t -> elt option
45
-
val max_elt : t -> elt
46
-
val max_elt_opt : t -> elt option
47
-
val choose : t -> elt
48
-
val choose_opt : t -> elt option
49
-
val split : elt -> t -> t * bool * t
50
-
val find : elt -> t -> elt
51
-
val find_opt : elt -> t -> elt option
52
-
val find_first : (elt -> bool) -> t -> elt
53
-
val find_first_opt : (elt -> bool) -> t -> elt option
54
-
val find_last : (elt -> bool) -> t -> elt
55
-
val find_last_opt : (elt -> bool) -> t -> elt option
56
-
val of_list : elt list -> t
57
-
val to_seq_from : elt -> t -> elt Seq.t
58
-
val to_seq : t -> elt Seq.t
59
-
val to_rev_seq : t -> elt Seq.t
60
-
val add_seq : elt Seq.t -> t -> t
61
-
val of_seq : elt Seq.t -> t
28
+
...
62
29
end
63
30
```
64
31
65
-
To create a set you need to start somewhere so here is the empty set:
32
+
After naming the newly-created module `StringSet`, OCaml's toplevel displays the module's signature. Since it contains a large number of functions, the output copied here is shortened for brevity (`...`).
33
+
34
+
This module also defines two types:
35
+
-`type elt = string` for the elements, and
36
+
-`type t = Set.Make(String).t` for the sets.
37
+
38
+
## Creating a Set
39
+
40
+
1. We can create an empty set using `StringSet.empty`:
41
+
```ocaml
42
+
# StringSet.empty ;;
43
+
- : StringSet.t = <abstr>
44
+
45
+
# StringSet.empty |> StringSet.to_list;;
46
+
- : string list = []
47
+
```
48
+
49
+
For `StringSet.empty`, you can see that the OCaml toplevel displays the placeholder `<abstr>` instead of the actual value. However, converting the string set to a list using `StringSet.to_list` results in an empty list.
50
+
51
+
2. A set with a single element is created using `StringSet.singleton`:
Alternatively if we know an element to start with we can create a set
73
-
like
89
+
The function `StringSet.add` with type `string -> StringSet.t -> StringSet.t` takes both a string and a string set. It returns a new string set. Sets created with the `Set.Make` functor in OCaml are immutable, so every time you add or remove an element from a set, a new set is created. The old value is unchanged.
The function `StringSet.remove` with type `string -> StringSet.t -> StringSet.t` takes both a string and a string set. It returns a new string set without the given string.
If we want to check and see if an element is in the set it might look
122
-
like this.
137
+
The function `StringSet.filter` of type `(string -> bool) -> StringSet.t -> StringSet.t` creates a new set by keeping the elements that satisfy a predicate from an existing set.
138
+
139
+
### Checking if an Element is Contained in a Set
123
140
124
141
```ocaml
125
-
# SS.mem "hello" s2;;
142
+
# ["good morning"; "hello"; "hi"]
143
+
|> StringSet.of_list
144
+
|> StringSet.mem "hello";;
126
145
- : bool = true
127
146
```
128
147
129
-
The Set module also provides the set theoretic operations union,
130
-
intersection and difference. For example, the difference of the original
131
-
set and the set with short strings (≤ 5 characters) is the set of long
132
-
strings:
148
+
To check if an element is contained in a set, use the `StringSet.mem` function.
149
+
150
+
## Sets With Custom Comparators
151
+
152
+
The `Set.Make` functor expects a module with two definitions: a type `t`
153
+
that represents the element type and the function `compare`,
154
+
whose signature is `t -> t -> int`. The
155
+
`String` module matches that structure, so we could
156
+
directly pass `String` as an argument to `Set.Make`. Incidentally, many
157
+
other modules also have that structure, including `Int` and `Float`,
158
+
so they too can be directly passed into `Set.Make` to construct a corresponding set module.
159
+
160
+
The `StringSet` module we created uses the built-in `compare` function provided by the `String` module.
161
+
162
+
Let's say we want to create a set of strings that performs a case-insensitive
163
+
comparison instead of the case-sensitive comparison provided by `String.compare`.
164
+
165
+
We can accomplish this by passing an ad-hoc module to the `Set.Make` function:
166
+
167
+
```ocaml
168
+
# module CISS = Set.Make(struct
169
+
type t = string
170
+
let compare a b = compare (String.lowercase_ascii a) (String.lowercase_ascii b)
171
+
end);;
172
+
```
173
+
174
+
We name the resulting module `CISS` (short for "Case Insensitive String Set").
175
+
176
+
You can see that this module has the intended behavior:
The value `"HELLO"` is not added to the set because it is considered equal to the value `"hello"`, which is already contained in the set.
183
+
184
+
You can use any type for elements, as long as you define a meaningful `compare` operation:
185
+
```ocaml
186
+
# type color = Red | Green | Blue;;
187
+
type color = Red | Green | Blue
188
+
189
+
# module SC = Set.Make(struct
190
+
type t = color
191
+
let compare a b =
192
+
match a, b with
193
+
| (Red, Red) -> 0
194
+
| (Red, Green) -> 1
195
+
| (Red, Blue) -> 1
196
+
| (Green, Red) -> -1
197
+
| (Green, Green) -> 0
198
+
| (Green, Blue) -> 1
199
+
| (Blue, Red) -> -1
200
+
| (Blue, Green) -> -1
201
+
| (Blue, Blue) -> 0
202
+
end);;
203
+
...
204
+
```
205
+
206
+
## Conclusion
140
207
141
-
Note that the Set module provides a purely functional data structure:
142
-
removing an element from a set does not alter that set but, rather,
143
-
returns a new set that is very similar to (and shares much of its
144
-
internals with) the original set.
208
+
We gave an overview of the `Set` module in OCaml by creating a `StringSet` module using the `Set.Make` functor. Further, we looked at how to create sets based on a custom comparison function. For more information, refer to [Set](https://ocaml.org/api/Set.Make.html) in the Standard Library documentation.
0 commit comments