mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-07 11:45:31 -05:00
more modern interface to Mixtbl; added a way to iterate on all bindings
This commit is contained in:
parent
cad578840e
commit
e74c85e3d2
3 changed files with 91 additions and 68 deletions
|
|
@ -26,35 +26,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
(** {1 Hash Table with Heterogeneous Keys} *)
|
(** {1 Hash Table with Heterogeneous Keys} *)
|
||||||
|
|
||||||
type 'a t = ('a, (unit -> unit)) Hashtbl.t
|
type 'b injection = {
|
||||||
|
get : (unit -> unit) -> 'b option;
|
||||||
type ('a, 'b) injection = {
|
set : 'b -> (unit -> unit);
|
||||||
getter : 'a t -> 'a -> 'b option;
|
|
||||||
setter : 'a t -> 'a -> 'b -> unit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type 'a t = ('a, unit -> unit) Hashtbl.t
|
||||||
|
|
||||||
let create n = Hashtbl.create n
|
let create n = Hashtbl.create n
|
||||||
|
|
||||||
let access () =
|
let create_inj () =
|
||||||
let r = ref None in
|
let r = ref None in
|
||||||
let getter tbl k =
|
let get f =
|
||||||
r := None; (* reset state in case last operation was not a get *)
|
r := None;
|
||||||
try
|
f ();
|
||||||
(Hashtbl.find tbl k) ();
|
!r
|
||||||
let result = !r in
|
and set v =
|
||||||
r := None; (* clean up here in order to avoid memory leak *)
|
(fun () -> r := Some v)
|
||||||
result
|
|
||||||
with Not_found -> None
|
|
||||||
in
|
in
|
||||||
let setter tbl k v =
|
{get;set}
|
||||||
let v_opt = Some v in
|
|
||||||
Hashtbl.replace tbl k (fun () -> r := v_opt)
|
|
||||||
in
|
|
||||||
{ getter; setter; }
|
|
||||||
|
|
||||||
let get ~inj tbl x = inj.getter tbl x
|
let get ~inj tbl x =
|
||||||
|
try inj.get (Hashtbl.find tbl x)
|
||||||
|
with Not_found -> None
|
||||||
|
|
||||||
let set ~inj tbl x y = inj.setter tbl x y
|
let set ~inj tbl x y =
|
||||||
|
Hashtbl.replace tbl x (inj.set y)
|
||||||
|
|
||||||
let length tbl = Hashtbl.length tbl
|
let length tbl = Hashtbl.length tbl
|
||||||
|
|
||||||
|
|
@ -65,14 +62,14 @@ let remove tbl x = Hashtbl.remove tbl x
|
||||||
let copy tbl = Hashtbl.copy tbl
|
let copy tbl = Hashtbl.copy tbl
|
||||||
|
|
||||||
let mem ~inj tbl x =
|
let mem ~inj tbl x =
|
||||||
match inj.getter tbl x with
|
try
|
||||||
| None -> false
|
inj.get (Hashtbl.find tbl x) <> None
|
||||||
| Some _ -> true
|
with Not_found -> false
|
||||||
|
|
||||||
let find ~inj tbl x =
|
let find ~inj tbl x =
|
||||||
match inj.getter tbl x with
|
match inj.get (Hashtbl.find tbl x) with
|
||||||
| None -> raise Not_found
|
| None -> raise Not_found
|
||||||
| Some y -> y
|
| Some v -> v
|
||||||
|
|
||||||
let iter_keys tbl f =
|
let iter_keys tbl f =
|
||||||
Hashtbl.iter (fun x _ -> f x) tbl
|
Hashtbl.iter (fun x _ -> f x) tbl
|
||||||
|
|
@ -80,12 +77,27 @@ let iter_keys tbl f =
|
||||||
let fold_keys tbl acc f =
|
let fold_keys tbl acc f =
|
||||||
Hashtbl.fold (fun x _ acc -> f acc x) tbl acc
|
Hashtbl.fold (fun x _ acc -> f acc x) tbl acc
|
||||||
|
|
||||||
let keys tbl =
|
(** {2 Iterators} *)
|
||||||
Hashtbl.fold (fun x _ acc -> x :: acc) tbl []
|
|
||||||
|
|
||||||
let bindings ~inj tbl =
|
type 'a sequence = ('a -> unit) -> unit
|
||||||
fold_keys tbl []
|
|
||||||
(fun acc k ->
|
let keys_seq tbl yield =
|
||||||
match inj.getter tbl k with
|
Hashtbl.iter
|
||||||
| None -> acc
|
(fun x _ -> yield x)
|
||||||
| Some v -> (k, v) :: acc)
|
tbl
|
||||||
|
|
||||||
|
let bindings_of ~inj tbl yield =
|
||||||
|
Hashtbl.iter
|
||||||
|
(fun k value ->
|
||||||
|
match inj.get value with
|
||||||
|
| None -> ()
|
||||||
|
| Some v -> yield (k, v)
|
||||||
|
) tbl
|
||||||
|
|
||||||
|
type value =
|
||||||
|
| Value : ('b injection -> 'b option) -> value
|
||||||
|
|
||||||
|
let bindings tbl yield =
|
||||||
|
Hashtbl.iter
|
||||||
|
(fun x y -> yield (x, Value (fun inj -> inj.get y)))
|
||||||
|
tbl
|
||||||
|
|
|
||||||
|
|
@ -58,28 +58,33 @@ type 'a t
|
||||||
(** A hash table containing values of different types.
|
(** A hash table containing values of different types.
|
||||||
The type parameter ['a] represents the type of the keys. *)
|
The type parameter ['a] represents the type of the keys. *)
|
||||||
|
|
||||||
type ('a, 'b) injection
|
type 'b injection
|
||||||
(** An accessor for values of type 'b in the table. Values put
|
(** An accessor for values of type 'b in any table. Values put
|
||||||
in the table using an injection can only be retrieved using this
|
in the table using an key can only be retrieved using this
|
||||||
very same injection. *)
|
very same key. *)
|
||||||
|
|
||||||
val create : int -> 'a t
|
val create : int -> 'a t
|
||||||
(** [create n] creates a hash table of initial size [n]. *)
|
(** [create n] creates a hash table of initial size [n]. *)
|
||||||
|
|
||||||
val access : unit -> ('a, 'b) injection
|
val create_inj : unit -> 'b injection
|
||||||
(** Return a value that works for a given type of values. This function is
|
(** Return a value that works for a given type of values. This function is
|
||||||
normally called once for each type of value. Several injections may be
|
normally called once for each type of value. Several keys may be
|
||||||
created for the same type, but a value set with a given setter can only be
|
created for the same type, but a value set with a given setter can only be
|
||||||
retrieved with the matching getter. The same injection can be reused
|
retrieved with the matching getter. The same key can be reused
|
||||||
across multiple tables (although not in a thread-safe way). *)
|
across multiple tables (although not in a thread-safe way). *)
|
||||||
|
|
||||||
val get : inj:('a, 'b) injection -> 'a t -> 'a -> 'b option
|
val get : inj:'b injection -> 'a t -> 'a -> 'b option
|
||||||
(** Get the value corresponding to this key, if it exists and
|
(** Get the value corresponding to this key, if it exists and
|
||||||
belongs to the same injection *)
|
belongs to the same key *)
|
||||||
|
|
||||||
val set : inj:('a, 'b) injection -> 'a t -> 'a -> 'b -> unit
|
val set : inj:'b injection -> 'a t -> 'a -> 'b -> unit
|
||||||
(** Bind the key to the value, using [inj] *)
|
(** Bind the key to the value, using [inj] *)
|
||||||
|
|
||||||
|
val find : inj:'b injection -> 'a t -> 'a -> 'b
|
||||||
|
(** Find the value for the given key, which must be of the right type.
|
||||||
|
raises Not_found if either the key is not found, or if its value
|
||||||
|
doesn't belong to the right type *)
|
||||||
|
|
||||||
val length : 'a t -> int
|
val length : 'a t -> int
|
||||||
(** Number of bindings *)
|
(** Number of bindings *)
|
||||||
|
|
||||||
|
|
@ -92,22 +97,27 @@ val remove : 'a t -> 'a -> unit
|
||||||
val copy : 'a t -> 'a t
|
val copy : 'a t -> 'a t
|
||||||
(** Copy of the table *)
|
(** Copy of the table *)
|
||||||
|
|
||||||
val mem : inj:('a, _) injection -> 'a t -> 'a -> bool
|
val mem : inj:_ injection-> 'a t -> 'a -> bool
|
||||||
(** Is the given key in the table, with the right type? *)
|
(** Is the given key in the table, with the right type? *)
|
||||||
|
|
||||||
val find : inj:('a, 'b) injection -> 'a t -> 'a -> 'b
|
|
||||||
(** Find the value for the given key, which must be of the right type.
|
|
||||||
raises Not_found if either the key is not found, or if its value
|
|
||||||
doesn't belong to the right type *)
|
|
||||||
|
|
||||||
val iter_keys : 'a t -> ('a -> unit) -> unit
|
val iter_keys : 'a t -> ('a -> unit) -> unit
|
||||||
(** Iterate on the keys of this table *)
|
(** Iterate on the keys of this table *)
|
||||||
|
|
||||||
val fold_keys : 'a t -> 'b -> ('b -> 'a -> 'b) -> 'b
|
val fold_keys : 'a t -> 'b -> ('b -> 'a -> 'b) -> 'b
|
||||||
(** Fold over the keys *)
|
(** Fold over the keys *)
|
||||||
|
|
||||||
val keys : 'a t -> 'a list
|
(** {2 Iterators} *)
|
||||||
(** List of the keys *)
|
|
||||||
|
|
||||||
val bindings : inj:('a, 'b) injection -> 'a t -> ('a * 'b) list
|
type 'a sequence = ('a -> unit) -> unit
|
||||||
|
|
||||||
|
val keys_seq : 'a t -> 'a sequence
|
||||||
|
(** All the keys *)
|
||||||
|
|
||||||
|
val bindings_of : inj:'b injection -> 'a t -> ('a * 'b) sequence
|
||||||
(** All the bindings that come from the corresponding injection *)
|
(** All the bindings that come from the corresponding injection *)
|
||||||
|
|
||||||
|
type value =
|
||||||
|
| Value : ('b injection -> 'b option) -> value
|
||||||
|
|
||||||
|
val bindings : 'a t -> ('a * value) sequence
|
||||||
|
(** Iterate on all bindings *)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
|
|
||||||
open OUnit
|
open OUnit
|
||||||
open Containers_misc
|
open Containers_misc
|
||||||
|
open CCFun
|
||||||
|
|
||||||
let example () =
|
let example () =
|
||||||
let inj_int = Mixtbl.access () in
|
let inj_int = Mixtbl.create_inj () in
|
||||||
let tbl = Mixtbl.create 10 in
|
let tbl = Mixtbl.create 10 in
|
||||||
OUnit.assert_equal None (Mixtbl.get ~inj:inj_int tbl "a");
|
OUnit.assert_equal None (Mixtbl.get ~inj:inj_int tbl "a");
|
||||||
Mixtbl.set inj_int tbl "a" 1;
|
Mixtbl.set inj_int tbl "a" 1;
|
||||||
OUnit.assert_equal (Some 1) (Mixtbl.get ~inj:inj_int tbl "a");
|
OUnit.assert_equal (Some 1) (Mixtbl.get ~inj:inj_int tbl "a");
|
||||||
let inj_string = Mixtbl.access () in
|
let inj_string = Mixtbl.create_inj () in
|
||||||
Mixtbl.set inj_string tbl "b" "Hello";
|
Mixtbl.set inj_string tbl "b" "Hello";
|
||||||
OUnit.assert_equal (Some "Hello") (Mixtbl.get inj_string tbl "b");
|
OUnit.assert_equal (Some "Hello") (Mixtbl.get inj_string tbl "b");
|
||||||
OUnit.assert_equal None (Mixtbl.get inj_string tbl "a");
|
OUnit.assert_equal None (Mixtbl.get inj_string tbl "a");
|
||||||
|
|
@ -19,7 +20,7 @@ let example () =
|
||||||
()
|
()
|
||||||
|
|
||||||
let test_length () =
|
let test_length () =
|
||||||
let inj_int = Mixtbl.access () in
|
let inj_int = Mixtbl.create_inj () in
|
||||||
let tbl = Mixtbl.create 5 in
|
let tbl = Mixtbl.create 5 in
|
||||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||||
|
|
@ -32,8 +33,8 @@ let test_length () =
|
||||||
()
|
()
|
||||||
|
|
||||||
let test_clear () =
|
let test_clear () =
|
||||||
let inj_int = Mixtbl.access () in
|
let inj_int = Mixtbl.create_inj () in
|
||||||
let inj_str = Mixtbl.access () in
|
let inj_str = Mixtbl.create_inj () in
|
||||||
let tbl = Mixtbl.create 5 in
|
let tbl = Mixtbl.create 5 in
|
||||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||||
|
|
@ -44,8 +45,8 @@ let test_clear () =
|
||||||
()
|
()
|
||||||
|
|
||||||
let test_mem () =
|
let test_mem () =
|
||||||
let inj_int = Mixtbl.access () in
|
let inj_int = Mixtbl.create_inj () in
|
||||||
let inj_str = Mixtbl.access () in
|
let inj_str = Mixtbl.create_inj () in
|
||||||
let tbl = Mixtbl.create 5 in
|
let tbl = Mixtbl.create 5 in
|
||||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||||
|
|
@ -59,27 +60,27 @@ let test_mem () =
|
||||||
()
|
()
|
||||||
|
|
||||||
let test_keys () =
|
let test_keys () =
|
||||||
let inj_int = Mixtbl.access () in
|
let inj_int = Mixtbl.create_inj () in
|
||||||
let inj_str = Mixtbl.access () in
|
let inj_str = Mixtbl.create_inj () in
|
||||||
let tbl = Mixtbl.create 5 in
|
let tbl = Mixtbl.create 5 in
|
||||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||||
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
||||||
let l = Mixtbl.keys tbl in
|
let l = Mixtbl.keys_seq tbl |> CCSequence.to_list in
|
||||||
OUnit.assert_equal ["baaz"; "bar"; "foo"] (List.sort compare l);
|
OUnit.assert_equal ["baaz"; "bar"; "foo"] (List.sort compare l);
|
||||||
()
|
()
|
||||||
|
|
||||||
let test_bindings () =
|
let test_bindings () =
|
||||||
let inj_int = Mixtbl.access () in
|
let inj_int = Mixtbl.create_inj () in
|
||||||
let inj_str = Mixtbl.access () in
|
let inj_str = Mixtbl.create_inj () in
|
||||||
let tbl = Mixtbl.create 5 in
|
let tbl = Mixtbl.create 5 in
|
||||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||||
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
||||||
Mixtbl.set ~inj:inj_str tbl "str" "rts";
|
Mixtbl.set ~inj:inj_str tbl "str" "rts";
|
||||||
let l_int = Mixtbl.bindings tbl ~inj:inj_int in
|
let l_int = Mixtbl.bindings_of tbl ~inj:inj_int |> CCSequence.to_list in
|
||||||
OUnit.assert_equal ["bar", 2; "foo", 1] (List.sort compare l_int);
|
OUnit.assert_equal ["bar", 2; "foo", 1] (List.sort compare l_int);
|
||||||
let l_str = Mixtbl.bindings tbl ~inj:inj_str in
|
let l_str = Mixtbl.bindings_of tbl ~inj:inj_str |> CCSequence.to_list in
|
||||||
OUnit.assert_equal ["baaz", "hello"; "str", "rts"] (List.sort compare l_str);
|
OUnit.assert_equal ["baaz", "hello"; "str", "rts"] (List.sort compare l_str);
|
||||||
()
|
()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue