more modern interface to Mixtbl; added a way to iterate on all bindings

This commit is contained in:
Simon Cruanes 2014-11-19 17:44:55 +01:00
parent cad578840e
commit e74c85e3d2
3 changed files with 91 additions and 68 deletions

View file

@ -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

View file

@ -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 *)

View file

@ -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);
() ()