mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 03:05:28 -05:00
import Mixtbl from its repository, into misc/
This commit is contained in:
parent
a47bd108ec
commit
cad578840e
5 changed files with 300 additions and 1 deletions
2
_oasis
2
_oasis
|
|
@ -81,7 +81,7 @@ Library "containers_misc"
|
|||
PHashtbl, SkipList, SplayTree, SplayMap, Univ,
|
||||
Bij, PiCalculus, RAL, UnionFind, SmallSet, AbsSet, CSM,
|
||||
TTree, PrintBox, HGraph, Automaton, Conv, Bidir, Iteratee,
|
||||
BTree, Ty, Cause, AVL, ParseReact
|
||||
BTree, Ty, Cause, AVL, ParseReact, Mixtbl
|
||||
BuildDepends: unix,containers
|
||||
FindlibName: misc
|
||||
FindlibParent: containers
|
||||
|
|
|
|||
91
misc/mixtbl.ml
Normal file
91
misc/mixtbl.ml
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
(*
|
||||
copyright (c) 2013-2014, simon cruanes
|
||||
all rights reserved.
|
||||
|
||||
redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer. redistributions in binary
|
||||
form must reproduce the above copyright notice, this list of conditions and the
|
||||
following disclaimer in the documentation and/or other materials provided with
|
||||
the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*)
|
||||
|
||||
(** {1 Hash Table with Heterogeneous Keys} *)
|
||||
|
||||
type 'a t = ('a, (unit -> unit)) Hashtbl.t
|
||||
|
||||
type ('a, 'b) injection = {
|
||||
getter : 'a t -> 'a -> 'b option;
|
||||
setter : 'a t -> 'a -> 'b -> unit;
|
||||
}
|
||||
|
||||
let create n = Hashtbl.create n
|
||||
|
||||
let access () =
|
||||
let r = ref None in
|
||||
let getter tbl k =
|
||||
r := None; (* reset state in case last operation was not a get *)
|
||||
try
|
||||
(Hashtbl.find tbl k) ();
|
||||
let result = !r in
|
||||
r := None; (* clean up here in order to avoid memory leak *)
|
||||
result
|
||||
with Not_found -> None
|
||||
in
|
||||
let setter tbl k v =
|
||||
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 set ~inj tbl x y = inj.setter tbl x y
|
||||
|
||||
let length tbl = Hashtbl.length tbl
|
||||
|
||||
let clear tbl = Hashtbl.clear tbl
|
||||
|
||||
let remove tbl x = Hashtbl.remove tbl x
|
||||
|
||||
let copy tbl = Hashtbl.copy tbl
|
||||
|
||||
let mem ~inj tbl x =
|
||||
match inj.getter tbl x with
|
||||
| None -> false
|
||||
| Some _ -> true
|
||||
|
||||
let find ~inj tbl x =
|
||||
match inj.getter tbl x with
|
||||
| None -> raise Not_found
|
||||
| Some y -> y
|
||||
|
||||
let iter_keys tbl f =
|
||||
Hashtbl.iter (fun x _ -> f x) tbl
|
||||
|
||||
let fold_keys tbl acc f =
|
||||
Hashtbl.fold (fun x _ acc -> f acc x) tbl acc
|
||||
|
||||
let keys tbl =
|
||||
Hashtbl.fold (fun x _ acc -> x :: acc) tbl []
|
||||
|
||||
let bindings ~inj tbl =
|
||||
fold_keys tbl []
|
||||
(fun acc k ->
|
||||
match inj.getter tbl k with
|
||||
| None -> acc
|
||||
| Some v -> (k, v) :: acc)
|
||||
113
misc/mixtbl.mli
Normal file
113
misc/mixtbl.mli
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
(*
|
||||
copyright (c) 2013-2014, simon cruanes
|
||||
all rights reserved.
|
||||
|
||||
redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer. redistributions in binary
|
||||
form must reproduce the above copyright notice, this list of conditions and the
|
||||
following disclaimer in the documentation and/or other materials provided with
|
||||
the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*)
|
||||
|
||||
(** {1 Hash Table with Heterogeneous Keys}
|
||||
|
||||
From https://github.com/mjambon/mixtbl , thanks to him.
|
||||
Example:
|
||||
|
||||
{[
|
||||
let inj_int = Mixtbl.access () ;;
|
||||
|
||||
let tbl = Mixtbl.create 10 ;;
|
||||
|
||||
OUnit.assert_equal None (Mixtbl.get ~inj:inj_int tbl "a");;
|
||||
|
||||
Mixtbl.set inj_int tbl "a" 1;;
|
||||
|
||||
OUnit.assert_equal (Some 1) (Mixtbl.get ~inj:inj_int tbl "a");;
|
||||
|
||||
let inj_string = Mixtbl.access () ;;
|
||||
|
||||
Mixtbl.set inj_string tbl "b" "Hello";
|
||||
|
||||
OUnit.assert_equal (Some "Hello") (Mixtbl.get inj_string tbl "b");;
|
||||
OUnit.assert_equal None (Mixtbl.get inj_string tbl "a");;
|
||||
OUnit.assert_equal (Some 1) (Mixtbl.get inj_int tbl "a");;
|
||||
Mixtbl.set inj_string tbl "a" "Bye";;
|
||||
|
||||
OUnit.assert_equal None (Mixtbl.get inj_int tbl "a");;
|
||||
OUnit.assert_equal (Some "Bye") (Mixtbl.get inj_string tbl "a");;
|
||||
]}
|
||||
|
||||
@since NEXT_RELEASE *)
|
||||
|
||||
type 'a t
|
||||
(** A hash table containing values of different types.
|
||||
The type parameter ['a] represents the type of the keys. *)
|
||||
|
||||
type ('a, 'b) injection
|
||||
(** An accessor for values of type 'b in the table. Values put
|
||||
in the table using an injection can only be retrieved using this
|
||||
very same injection. *)
|
||||
|
||||
val create : int -> 'a t
|
||||
(** [create n] creates a hash table of initial size [n]. *)
|
||||
|
||||
val access : unit -> ('a, 'b) injection
|
||||
(** 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
|
||||
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
|
||||
across multiple tables (although not in a thread-safe way). *)
|
||||
|
||||
val get : inj:('a, 'b) injection -> 'a t -> 'a -> 'b option
|
||||
(** Get the value corresponding to this key, if it exists and
|
||||
belongs to the same injection *)
|
||||
|
||||
val set : inj:('a, 'b) injection -> 'a t -> 'a -> 'b -> unit
|
||||
(** Bind the key to the value, using [inj] *)
|
||||
|
||||
val length : 'a t -> int
|
||||
(** Number of bindings *)
|
||||
|
||||
val clear : 'a t -> unit
|
||||
(** Clear content of the hashtable *)
|
||||
|
||||
val remove : 'a t -> 'a -> unit
|
||||
(** Remove the binding for this key *)
|
||||
|
||||
val copy : 'a t -> 'a t
|
||||
(** Copy of the table *)
|
||||
|
||||
val mem : inj:('a, _) injection -> 'a t -> 'a -> bool
|
||||
(** 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
|
||||
(** Iterate on the keys of this table *)
|
||||
|
||||
val fold_keys : 'a t -> 'b -> ('b -> 'a -> 'b) -> 'b
|
||||
(** Fold over the keys *)
|
||||
|
||||
val keys : 'a t -> 'a list
|
||||
(** List of the keys *)
|
||||
|
||||
val bindings : inj:('a, 'b) injection -> 'a t -> ('a * 'b) list
|
||||
(** All the bindings that come from the corresponding injection *)
|
||||
|
|
@ -21,6 +21,7 @@ let suite =
|
|||
Test_heap.suite;
|
||||
Test_graph.suite;
|
||||
Test_univ.suite;
|
||||
Test_mixtbl.suite;
|
||||
]
|
||||
|
||||
let props =
|
||||
|
|
|
|||
94
tests/test_mixtbl.ml
Normal file
94
tests/test_mixtbl.ml
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
open OUnit
|
||||
open Containers_misc
|
||||
|
||||
let example () =
|
||||
let inj_int = Mixtbl.access () in
|
||||
let tbl = Mixtbl.create 10 in
|
||||
OUnit.assert_equal None (Mixtbl.get ~inj:inj_int tbl "a");
|
||||
Mixtbl.set inj_int tbl "a" 1;
|
||||
OUnit.assert_equal (Some 1) (Mixtbl.get ~inj:inj_int tbl "a");
|
||||
let inj_string = Mixtbl.access () in
|
||||
Mixtbl.set inj_string tbl "b" "Hello";
|
||||
OUnit.assert_equal (Some "Hello") (Mixtbl.get inj_string tbl "b");
|
||||
OUnit.assert_equal None (Mixtbl.get inj_string tbl "a");
|
||||
OUnit.assert_equal (Some 1) (Mixtbl.get inj_int tbl "a");
|
||||
Mixtbl.set inj_string tbl "a" "Bye";
|
||||
OUnit.assert_equal None (Mixtbl.get inj_int tbl "a");
|
||||
OUnit.assert_equal (Some "Bye") (Mixtbl.get inj_string tbl "a");
|
||||
()
|
||||
|
||||
let test_length () =
|
||||
let inj_int = Mixtbl.access () in
|
||||
let tbl = Mixtbl.create 5 in
|
||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||
OUnit.assert_equal 2 (Mixtbl.length tbl);
|
||||
OUnit.assert_equal 2 (Mixtbl.find ~inj:inj_int tbl "bar");
|
||||
Mixtbl.set ~inj:inj_int tbl "foo" 42;
|
||||
OUnit.assert_equal 2 (Mixtbl.length tbl);
|
||||
Mixtbl.remove tbl "bar";
|
||||
OUnit.assert_equal 1 (Mixtbl.length tbl);
|
||||
()
|
||||
|
||||
let test_clear () =
|
||||
let inj_int = Mixtbl.access () in
|
||||
let inj_str = Mixtbl.access () in
|
||||
let tbl = Mixtbl.create 5 in
|
||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
||||
OUnit.assert_equal 3 (Mixtbl.length tbl);
|
||||
Mixtbl.clear tbl;
|
||||
OUnit.assert_equal 0 (Mixtbl.length tbl);
|
||||
()
|
||||
|
||||
let test_mem () =
|
||||
let inj_int = Mixtbl.access () in
|
||||
let inj_str = Mixtbl.access () in
|
||||
let tbl = Mixtbl.create 5 in
|
||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
||||
OUnit.assert_bool "mem foo int" (Mixtbl.mem ~inj:inj_int tbl "foo");
|
||||
OUnit.assert_bool "mem bar int" (Mixtbl.mem ~inj:inj_int tbl "bar");
|
||||
OUnit.assert_bool "not mem baaz int" (not (Mixtbl.mem ~inj:inj_int tbl "baaz"));
|
||||
OUnit.assert_bool "not mem foo str" (not (Mixtbl.mem ~inj:inj_str tbl "foo"));
|
||||
OUnit.assert_bool "not mem bar str" (not (Mixtbl.mem ~inj:inj_str tbl "bar"));
|
||||
OUnit.assert_bool "mem baaz str" (Mixtbl.mem ~inj:inj_str tbl "baaz");
|
||||
()
|
||||
|
||||
let test_keys () =
|
||||
let inj_int = Mixtbl.access () in
|
||||
let inj_str = Mixtbl.access () in
|
||||
let tbl = Mixtbl.create 5 in
|
||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
||||
let l = Mixtbl.keys tbl in
|
||||
OUnit.assert_equal ["baaz"; "bar"; "foo"] (List.sort compare l);
|
||||
()
|
||||
|
||||
let test_bindings () =
|
||||
let inj_int = Mixtbl.access () in
|
||||
let inj_str = Mixtbl.access () in
|
||||
let tbl = Mixtbl.create 5 in
|
||||
Mixtbl.set ~inj:inj_int tbl "foo" 1;
|
||||
Mixtbl.set ~inj:inj_int tbl "bar" 2;
|
||||
Mixtbl.set ~inj:inj_str tbl "baaz" "hello";
|
||||
Mixtbl.set ~inj:inj_str tbl "str" "rts";
|
||||
let l_int = Mixtbl.bindings tbl ~inj:inj_int in
|
||||
OUnit.assert_equal ["bar", 2; "foo", 1] (List.sort compare l_int);
|
||||
let l_str = Mixtbl.bindings tbl ~inj:inj_str in
|
||||
OUnit.assert_equal ["baaz", "hello"; "str", "rts"] (List.sort compare l_str);
|
||||
()
|
||||
|
||||
let suite =
|
||||
"mixtbl" >:::
|
||||
[ "example" >:: example;
|
||||
"length" >:: test_length;
|
||||
"clear" >:: test_clear;
|
||||
"mem" >:: test_mem;
|
||||
"bindings" >:: test_bindings;
|
||||
]
|
||||
|
||||
Loading…
Add table
Reference in a new issue