For the stdlib you could always do something ugly like this:
module S = Set.Make (String)
exception Done
let sole_element set =
let cell = ref None in
begin try
S.iter (fun x ->
match !cell with
| None -> cell := Some x
| Some _ -> cell := None; raise Done)
set;
with Done -> ()
end;
!cell
module String_set = struct
include Set.Make (String)
let is_singleton s =
match choose_opt s with
| None -> false
| Some e -> equal s (singleton e)
end
(* O(log n), a bit less polymorphic than necessary as it depends on equality of keys.
corrected: I thought it was O(1), gsg pointed out it is logarithmic. My bad! *)
let is_singleton s =
match split (choose s) s with
| exception Not_found -> false (* empty *)
| (l, _, r) -> is_empty l && is_empty r
(* polymorphic (it depends only on the structure of the container, not on elements),
but O(log n) as iter/fold has to traverse the left-most branch before starting to iterate *)
let is_singleton s =
match fold (fun elt count -> if count > 0 then raise Exit else count + 1) s 0 with
| 1 -> true
| _ -> false
| exception Exit -> false
Should work on Set and Map (slightly adapting the fold function, doesnβt depend on elements of the map).
Arrgghh, you are right. choose is indeed O(log n) (would work with sets with a unique structure, such as patricia :P).
It happens to be min_elt in current implementation.
Seems as if the exists approach is the least bad. I suppose that occasionally having to write this kind of ugly code is the downside of abstract data types with minimalist interfaces.