diff --git a/src/core/CCMap.ml b/src/core/CCMap.ml index 49b59b46..7df8dc58 100644 --- a/src/core/CCMap.ml +++ b/src/core/CCMap.ml @@ -12,7 +12,9 @@ module type S = sig include Map.S val get : key -> 'a t -> 'a option - (** Safe version of {!find}. *) + (** [get k m] returns [Some v] if the current binding of [k] in [m] is [v], + or [None] if the key [k] is not present. + Safe version of {!find}. *) val get_or : key -> 'a t -> default:'a -> 'a (** [get_or k m ~default] returns the value associated to [k] if present, @@ -26,28 +28,38 @@ module type S = sig [add k v' m] is returned. *) val choose_opt : 'a t -> (key * 'a) option - (** Safe version of {!choose}. + (** [choose_opt m] returns one binding of the given map [m], or [None] if [m] is empty. + Safe version of {!choose}. @since 1.5 *) val min_binding_opt : 'a t -> (key * 'a) option - (** Safe version of {!min_binding}. + (** [min_binding_opt m] returns the smallest binding of the given map [m], + or [None] if [m] is empty. + Safe version of {!min_binding}. @since 1.5 *) val max_binding_opt : 'a t -> (key * 'a) option - (** Safe version of {!max_binding}. + (** [max_binding_opt m] returns the largest binding of the given map [m], + or [None] if [m] is empty. + Safe version of {!max_binding}. @since 1.5 *) val find_opt : key -> 'a t -> 'a option - (** Safe version of {!find}. + (** [find_opt k m] returns [Some v] if the current binding of [k] in [m] is [v], + or [None] if the key [k] is not present. + Safe version of {!find}. @since 1.5 *) val find_first : (key -> bool) -> 'a t -> key * 'a - (** Find smallest binding satisfying the monotonic predicate. + (** [find_first f m] where [f] is a monotonically increasing function, returns the binding of [m] + with the lowest key [k] such that [f k], or raises [Not_found] if no such key exists. See {!Map.S.find_first}. @since 1.5 *) val find_first_opt : (key -> bool) -> 'a t -> (key * 'a) option - (** Safe version of {!find_first}. + (** [find_first_opt f m] where [f] is a monotonically increasing function, returns an option containing + the binding of [m] with the lowest key [k] such that [f k], or [None] if no such key exists. + Safe version of {!find_first}. @since 1.5 *) val merge_safe : @@ -56,51 +68,98 @@ module type S = sig (** [merge_safe ~f a b] merges the maps [a] and [b] together. @since 0.17 *) - val of_iter : (key * 'a) iter -> 'a t - (** Like {!of_list}. - @since 2.8 *) - val add_seq : 'a t -> (key * 'a) Seq.t -> 'a t - (** Like {!add_list}. - @since 2.8 *) + (** [add_seq m seq] adds the given [Seq.t] of bindings to the map [m]. + Like {!add_list}. + Renamed from [add_std_seq] since 3.0. + @since 3.0 *) + + val add_seq_with : f:(key -> 'a -> 'a -> 'a) -> 'a t -> (key * 'a) Seq.t -> 'a t + (** [add_seq ~f m l] adds the given seq [l] of bindings to the map [m], + using [f] to combine values that have the same key. + @since NEXT_RELEASE *) val of_seq : (key * 'a) Seq.t -> 'a t - (** Like {!of_list}. - @since 2.8 *) + (** [of_seq seq] builds a map from the given [Seq.t] of bindings. + Like {!of_list}. + Renamed from [of_std_seq] since 3.0. + @since 3.0 *) + + val of_seq_with : f:(key -> 'a -> 'a -> 'a) -> (key * 'a) Seq.t -> 'a t + (** [of_seq_with ~f l] builds a map from the given seq [l] of bindings [k_i -> v_i], + added in order using {!add}. + If a key occurs several times, all its bindings are combined using the + function [f], with [f key v1 v2] being called with [v1] occurring + later in the seq than [v2]. + @since NEXT_RELEASE *) val add_iter : 'a t -> (key * 'a) iter -> 'a t - (** Like {!add_list}. + (** [add_iter m iter] adds the given [iter] of bindings to the map [m]. + Like {!add_list}. @since 2.8 *) + val add_iter_with : f:(key -> 'a -> 'a -> 'a) -> 'a t -> (key * 'a) iter -> 'a t + (** [add_iter ~f m l] adds the given iter [l] of bindings to the map [m], + using [f] to combine values that have the same key. + @since NEXT_RELEASE *) + val of_iter : (key * 'a) iter -> 'a t - (** Like {!of_list}. + (** [of_iter iter] builds a map from the given [iter] of bindings. + Like {!of_list}. @since 2.8 *) + val of_iter_with : f:(key -> 'a -> 'a -> 'a) -> (key * 'a) iter -> 'a t + (** [of_iter_with ~f l] builds a map from the given iter [l] of bindings [k_i -> v_i], + added in order using {!add}. + If a key occurs several times, all its bindings are combined using the + function [f], with [f key v1 v2] being called with [v1] occurring + later in the iter than [v2]. + @since NEXT_RELEASE *) + val to_iter : 'a t -> (key * 'a) iter - (** Like {!to_list}. + (** [to_iter m] iterates on the whole map [m], creating an [iter] of bindings. + Like {!to_list}. @since 2.8 *) val of_list : (key * 'a) list -> 'a t - (** Build a map from the given list of bindings [k_i -> v_i], + (** [of_list l] builds a map from the given list [l] of bindings [k_i -> v_i], added in order using {!add}. If a key occurs several times, only its last binding will be present in the result. *) + val of_list_with : f:(key -> 'a -> 'a -> 'a) -> (key * 'a) list -> 'a t + (** [of_list_with ~f l] builds a map from the given list [l] of bindings [k_i -> v_i], + added in order using {!add}. + If a key occurs several times, all its bindings are combined using the + function [f], with [f key v1 v2] being called with [v1] occurring + later in the list than [v2]. + @since NEXT_RELEASE *) + val add_list : 'a t -> (key * 'a) list -> 'a t - (** @since 0.14 *) + (** [add_list m l] adds the given list [l] of bindings to the map [m]. + @since 0.14 *) + + val add_list_with : f:(key -> 'a -> 'a -> 'a) -> 'a t -> (key * 'a) list -> 'a t + (** [add_list ~f m l] adds the given list [l] of bindings to the map [m], + using [f] to combine values that have the same key. + @since NEXT_RELEASE *) val keys : _ t -> key iter - (** Iterate on keys only. + (** [keys m] iterates on the keys of [m] only, creating an [iter] of keys. @since 0.15 *) val values : 'a t -> 'a iter - (** Iterate on values only. + (** [values m] iterates on the values of [m] only, creating an [iter] of values. @since 0.15 *) val to_list : 'a t -> (key * 'a) list + (** [to_list m] builds a list of the bindings of the given map [m]. + The order is unspecified. *) val pp : ?pp_start:unit printer -> ?pp_stop:unit printer -> ?pp_arrow:unit printer -> ?pp_sep:unit printer -> key printer -> 'a printer -> 'a t printer + (** [pp ?pp_start ?pp_stop ?pp_arrow ?pp_sep pp_key pp_v m] pretty-prints the + contents of the map. *) end (*$inject @@ -210,14 +269,30 @@ module Make(O : Map.OrderedType) = struct Seq.iter (fun (k,v) -> m := add k v !m) s; !m + let add_seq_with ~f m s = + let m = ref m in + Seq.iter (fun (k,v) -> + let v = try f k v (find k !m) with Not_found -> v in + m := add k v !m) s; + !m + let of_seq s = add_seq empty s + let of_seq_with ~f s = add_seq_with ~f empty s let add_iter m s = let m = ref m in s (fun (k,v) -> m := add k v !m); !m + let add_iter_with ~f m s = + let m = ref m in + s (fun (k,v) -> + let v = try f k v (find k !m) with Not_found -> v in + m := add k v !m); + !m + let of_iter s = add_iter empty s + let of_iter_with ~f s = add_iter_with ~f empty s let to_iter m yield = iter (fun k v -> yield (k,v)) m @@ -229,8 +304,15 @@ module Make(O : Map.OrderedType) = struct iter (fun _ v -> yield v) m let add_list m l = List.fold_left (fun m (k,v) -> add k v m) m l + let add_list_with ~f m l = + List.fold_left + (fun m (k,v) -> + let v = try f k v (find k m) with Not_found -> v in + add k v m) + m l let of_list l = add_list empty l + let of_list_with ~f l = add_list_with ~f empty l let to_list m = fold (fun k v acc -> (k,v)::acc) m [] @@ -250,3 +332,18 @@ module Make(O : Map.OrderedType) = struct m; pp_stop fmt () end + +(*$inject + module M2 = Make(CCInt) +*) + +(*$Q + Q.(list (pair small_int small_int)) M2.(fun l -> \ + to_list (of_list l) = to_list (of_list_with ~f:(fun _ v _ ->v) l)) + Q.(list (pair small_int small_int)) M2.(fun l -> \ + to_list (of_iter @@ Iter.of_list l) = \ + to_list (of_iter_with ~f:(fun _ v _ ->v) @@ Iter.of_list l)) + Q.(list (pair small_int small_int)) M2.(fun l -> \ + to_list (of_seq @@ CCSeq.of_list l) = \ + to_list (of_seq_with ~f:(fun _ v _ ->v) @@ CCSeq.of_list l)) +*) diff --git a/src/core/CCMap.mli b/src/core/CCMap.mli index 4797e449..a7ebacb6 100644 --- a/src/core/CCMap.mli +++ b/src/core/CCMap.mli @@ -75,33 +75,54 @@ module type S = sig (** [merge_safe ~f a b] merges the maps [a] and [b] together. @since 0.17 *) - val of_iter : (key * 'a) iter -> 'a t - (** [of_iter iter] builds a map from the given [iter] of bindings. - Like {!of_list}. - @since 2.8 *) - val add_seq : 'a t -> (key * 'a) Seq.t -> 'a t (** [add_seq m seq] adds the given [Seq.t] of bindings to the map [m]. Like {!add_list}. Renamed from [add_std_seq] since 3.0. @since 3.0 *) + val add_seq_with : f:(key -> 'a -> 'a -> 'a) -> 'a t -> (key * 'a) Seq.t -> 'a t + (** [add_seq ~f m l] adds the given seq [l] of bindings to the map [m], + using [f] to combine values that have the same key. + @since NEXT_RELEASE *) + val of_seq : (key * 'a) Seq.t -> 'a t (** [of_seq seq] builds a map from the given [Seq.t] of bindings. Like {!of_list}. Renamed from [of_std_seq] since 3.0. @since 3.0 *) + val of_seq_with : f:(key -> 'a -> 'a -> 'a) -> (key * 'a) Seq.t -> 'a t + (** [of_seq_with ~f l] builds a map from the given seq [l] of bindings [k_i -> v_i], + added in order using {!add}. + If a key occurs several times, all its bindings are combined using the + function [f], with [f key v1 v2] being called with [v1] occurring + later in the seq than [v2]. + @since NEXT_RELEASE *) + val add_iter : 'a t -> (key * 'a) iter -> 'a t (** [add_iter m iter] adds the given [iter] of bindings to the map [m]. Like {!add_list}. @since 2.8 *) + val add_iter_with : f:(key -> 'a -> 'a -> 'a) -> 'a t -> (key * 'a) iter -> 'a t + (** [add_iter ~f m l] adds the given iter [l] of bindings to the map [m], + using [f] to combine values that have the same key. + @since NEXT_RELEASE *) + val of_iter : (key * 'a) iter -> 'a t (** [of_iter iter] builds a map from the given [iter] of bindings. Like {!of_list}. @since 2.8 *) + val of_iter_with : f:(key -> 'a -> 'a -> 'a) -> (key * 'a) iter -> 'a t + (** [of_iter_with ~f l] builds a map from the given iter [l] of bindings [k_i -> v_i], + added in order using {!add}. + If a key occurs several times, all its bindings are combined using the + function [f], with [f key v1 v2] being called with [v1] occurring + later in the iter than [v2]. + @since NEXT_RELEASE *) + val to_iter : 'a t -> (key * 'a) iter (** [to_iter m] iterates on the whole map [m], creating an [iter] of bindings. Like {!to_list}. @@ -113,10 +134,23 @@ module type S = sig If a key occurs several times, only its last binding will be present in the result. *) + val of_list_with : f:(key -> 'a -> 'a -> 'a) -> (key * 'a) list -> 'a t + (** [of_list_with ~f l] builds a map from the given list [l] of bindings [k_i -> v_i], + added in order using {!add}. + If a key occurs several times, all its bindings are combined using the + function [f], with [f key v1 v2] being called with [v1] occurring + later in the list than [v2]. + @since NEXT_RELEASE *) + val add_list : 'a t -> (key * 'a) list -> 'a t (** [add_list m l] adds the given list [l] of bindings to the map [m]. @since 0.14 *) + val add_list_with : f:(key -> 'a -> 'a -> 'a) -> 'a t -> (key * 'a) list -> 'a t + (** [add_list ~f m l] adds the given list [l] of bindings to the map [m], + using [f] to combine values that have the same key. + @since NEXT_RELEASE *) + val keys : _ t -> key iter (** [keys m] iterates on the keys of [m] only, creating an [iter] of keys. @since 0.15 *)