This commit is contained in:
madroach 2018-10-13 23:52:46 +00:00 committed by GitHub
commit e42ce3f311
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1401 additions and 208 deletions

View file

@ -1,8 +1,10 @@
PROMOTE=$(if $(shell ocamlc -version |grep '4\.0[012]\.[0-9][0-9]*'), \
--ignore-promoted-rules, )
all: build test
build:
dune build @install
dune build $(PROMOTE) @install
test: build
dune runtest --no-buffer --force
@ -11,12 +13,12 @@ clean:
dune clean
doc:
dune build @doc
dune build $(PROMOTE) @doc
BENCH_TARGETS=run_benchs.exe run_bench_hash.exe
benchs:
dune build $(addprefix benchs/, $(BENCH_TARGETS))
dune build $(PROMOTE) $(addprefix benchs/, $(BENCH_TARGETS))
examples:
dune build examples/id_sexp.exe

View file

@ -20,6 +20,7 @@ let do_not_test file =
is_suffix ~sub:"containers.ml" file ||
is_suffix ~sub:"containers_top.ml" file ||
is_suffix ~sub:"mkflags.ml" file ||
is_suffix ~sub:"unlabel.ml" file ||
is_suffix ~sub:"utop.ml" file
let prefix = "src"

View file

@ -1,3 +1,4 @@
(* AUTOGENERATED FROM CCArrayLabels.mli *)
(* This file is free software, part of containers. See file "license" for more details. *)
@ -13,6 +14,10 @@ type 'a printer = Format.formatter -> 'a -> unit
(** {2 Arrays} *)
(**/**)
external make_float : int -> float array = "caml_make_float_vect" (* compat *)
(**/**)
include module type of Array
type 'a t = 'a array
@ -57,28 +62,30 @@ val length : _ t -> int
(** [length a] returns the length (number of elements) of the given array [a]. *)
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** [fold f acc a] computes [f (... (f (f acc a.(0)) a.(1)) ...) a.(n-1)],
(** [fold f init a] computes [f (... (f (f init a.(0)) a.(1)) ...) a.(n-1)],
where [n] is the length of the array [a]. *)
val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** [foldi f acc a] is just like {!fold}, but it also passes in the index
(** [foldi f init a] is just like {!fold}, but it also passes in the index
of each element as the second argument to the folded function [f]. *)
val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a
(** [fold_while f acc a] folds left on array [a] until a stop condition via [('a, `Stop)]
(** [fold_while f init a] folds left on array [a] until a stop condition via [('a, `Stop)]
is indicated by the accumulator.
@since 0.8 *)
val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a t -> 'acc * 'b t
(** [fold_map f acc a] is a [fold_left]-like function, but it also maps the
(** [fold_map f init a] is a [fold_left]-like function, but it also maps the
array to another array.
@since 1.2 *)
@since 1.2, but only
@since 2.1 with labels *)
val scan_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'acc t
(** [scan_left f acc a] returns the array
[ [|acc; f acc x0; f (f acc a.(0)) a.(1); |] ].
(** [scan_left f init a] returns the array
[ [|init; f init x0; f (f init a.(0)) a.(1); |] ].
@since 1.2 *)
@since 1.2, but only
@since 2.1 with labels *)
val iter : ('a -> unit) -> 'a t -> unit
(** [iter f a] applies function [f] in turn to all elements of [a].
@ -103,24 +110,27 @@ val reverse_in_place : 'a t -> unit
(** [reverse_in_place a] reverses the array [a] in place. *)
val sorted : ('a -> 'a -> int) -> 'a t -> 'a array
(** [sorted cmp a] makes a copy of [a] and sorts it with [cmp].
(* FIXME: better label this ~cmp ?? *)
(** [sorted f a] makes a copy of [a] and sorts it with [f].
@since 1.0 *)
val sort_indices : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_indices cmp a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [sorted cmp a]
(* FIXME: better label this ~cmp ?? *)
(** [sort_indices f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [sorted f a]
appears in [a]. [a] is not modified.
In other words, [map (fun i -> a.(i)) (sort_indices cmp a) = sorted cmp a].
In other words, [map (fun i -> a.(i)) (sort_indices f a) = sorted f a].
[sort_indices] yields the inverse permutation of {!sort_ranking}.
@since 1.0 *)
val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_ranking cmp a] returns a new array [b], with the same length as [a],
(* FIXME: better label this ~cmp ?? *)
(** [sort_ranking f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [a] appears
in [sorted cmp a]. [a] is not modified.
in [sorted f a]. [a] is not modified.
In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a].
In other words, [map (fun i -> (sorted f a).(i)) (sort_ranking f a) = a].
[sort_ranking] yields the inverse permutation of {!sort_indices}.
In the absence of duplicate elements in [a], we also have
@ -130,97 +140,103 @@ val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array
val find_map : ('a -> 'b option) -> 'a t -> 'b option
(** [find_map f a] returns [Some y] if there is an element [x] such
that [f x = Some y]. Otherwise returns [None].
@since 1.3 *)
@since 1.3, but only
@since 2.1 with labels *)
val find : ('a -> 'b option) -> 'a t -> 'b option
(** [find f a] is an alias to {!find_map}.
@deprecated since 1.3, use {!find_map} instead. *)
@deprecated since 1.3, use {!find_map} instead.
The version with labels is
@deprecated since 2.1, use {!find_map} instead. *)
val find_map_i : (int -> 'a -> 'b option) -> 'a t -> 'b option
(** [find_map_i f a] is like {!find_map}, but the index of the element is also passed
to the predicate function [f].
@since 1.3 *)
@since 1.3, but only
@since 2.1 with labels *)
val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option
(** [findi f a] is an alias to {!find_map_i}.
@since 0.3.4
@deprecated since 1.3, use {!find_map_i} instead. *)
@deprecated since 1.3, use {!find_map} instead.
The version with labels is
@deprecated since 2.1, use {!find_map} instead. *)
val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p a] returns [Some (i,x)] where [x] is the [i]-th element of [a],
and [p x] holds. Otherwise returns [None].
(** [find_idx f a] returns [Some (i,x)] where [x] is the [i]-th element of [a],
and [f x] holds. Otherwise returns [None].
@since 0.3.4 *)
val lookup : cmp:'a ord -> 'a -> 'a t -> int option
(** [lookup cmp x a] lookups the index of some key [x] in a sorted array [a].
Undefined behavior if the array [a] is not sorted wrt [cmp].
(** [lookup ~cmp key a] lookups the index of some key [key] in a sorted array [a].
Undefined behavior if the array [a] is not sorted wrt [~cmp].
Complexity: [O(log (n))] (dichotomic search).
@return [None] if the key [x] is not present, or
@return [None] if the key [key] is not present, or
[Some i] ([i] the index of the key) otherwise. *)
val lookup_exn : cmp:'a ord -> 'a -> 'a t -> int
(** [lookup_exn cmp x a] is like {!lookup}, but
@raise Not_found if the key [x] is not present. *)
(** [lookup_exn ~cmp key a] is like {!lookup}, but
@raise Not_found if the key [key] is not present. *)
val bsearch : cmp:('a -> 'a -> int) -> 'a -> 'a t ->
[ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ]
(** [bsearch ~cmp x a] finds the index of the object [x] in the array [a],
provided [a] is {b sorted} using [cmp]. If the array is not sorted,
(** [bsearch ~cmp key a] finds the index of the object [key] in the array [a],
provided [a] is {b sorted} using [~cmp]. If the array is not sorted,
the result is not specified (may raise Invalid_argument).
Complexity: [O(log n)] where n is the length of the array [a]
(dichotomic search).
@return
- [`At i] if [cmp a.(i) x = 0] (for some i).
- [`All_lower] if all elements of [a] are lower than [x].
- [`All_bigger] if all elements of [a] are bigger than [x].
- [`Just_after i] if [a.(i) < x < a.(i+1)].
- [`At i] if [cmp a.(i) key = 0] (for some i).
- [`All_lower] if all elements of [a] are lower than [key].
- [`All_bigger] if all elements of [a] are bigger than [key].
- [`Just_after i] if [a.(i) < key < a.(i+1)].
- [`Empty] if the array [a] is empty.
@raise Invalid_argument if the array is found to be unsorted w.r.t [cmp].
@since 0.13 *)
val for_all : ('a -> bool) -> 'a t -> bool
(** [for_all p [|a1; ...; an|]] is [true] if all elements of the array
satisfy the predicate [p]. That is, it returns
[(p a1) && (p a2) && ... && (p an)]. *)
(** [for_all f [|a1; ...; an|]] is [true] if all elements of the array
satisfy the predicate [f]. That is, it returns
[(f a1) && (f a2) && ... && (f an)]. *)
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** [for_all2 p [|a1; ...; an|] [|b1; ...; bn|]] is [true] if each pair of elements [ai bi]
satisfies the predicate [p].
That is, it returns [(p a1 b1) && (p a2 b2) && ... && (p an bn)].
(** [for_all2 f [|a1; ...; an|] [|b1; ...; bn|]] is [true] if each pair of elements [ai bi]
satisfies the predicate [f].
That is, it returns [(f a1 b1) && (f a2 b2) && ... && (f an bn)].
@raise Invalid_argument if arrays have distinct lengths.
Allow different types.
@since 0.20 *)
val exists : ('a -> bool) -> 'a t -> bool
(** [exists p [|a1; ...; an|]] is [true] if at least one element of
the array satisfies the predicate [p]. That is, it returns
[(p a1) || (p a2) || ... || (p an)]. *)
(** [exists f [|a1; ...; an|]] is [true] if at least one element of
the array satisfies the predicate [f]. That is, it returns
[(f a1) || (f a2) || ... || (f an)]. *)
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** [exists2 p [|a1; ...; an|] [|b1; ...; bn|]] is [true] if any pair of elements [ai bi]
satisfies the predicate [p].
That is, it returns [(p a1 b1) || (p a2 b2) || ... || (p an bn)].
(** [exists2 f [|a1; ...; an|] [|b1; ...; bn|]] is [true] if any pair of elements [ai bi]
satisfies the predicate [f].
That is, it returns [(f a1 b1) || (f a2 b2) || ... || (f an bn)].
@raise Invalid_argument if arrays have distinct lengths.
Allow different types.
@since 0.20 *)
val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
(** [fold2 f acc a b] fold on two arrays [a] and [b] stepwise.
It computes [f (... (f acc a1 b1)...) an bn].
(** [fold2 f init a b] fold on two arrays [a] and [b] stepwise.
It computes [f (... (f init a1 b1)...) an bn].
@raise Invalid_argument if arrays have distinct lengths.
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *)
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
(** [iter2 f a b] iterates on the two arrays [a] and [b] stepwise.
It is equivalent to [f a0 b0; ...; f a.(length a - 1) b.(length b - 1); ()].
@raise Invalid_argument if arrays have distinct lengths.
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *)
val shuffle : 'a t -> unit
@ -275,8 +291,8 @@ val rev : 'a t -> 'a t
@since 0.20 *)
val filter : ('a -> bool) -> 'a t -> 'a t
(** [filter p a] filters elements out of the array [a]. Only the elements satisfying
the given predicate [p] will be kept. *)
(** [filter f a] filters elements out of the array [a]. Only the elements satisfying
the given predicate [f] will be kept. *)
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
(** [filter_map f [|a1; ...; an|]] calls [(f a1) ... (f an)] and returns an array [b] consisting
@ -328,6 +344,6 @@ end
val sort_generic :
(module MONO_ARRAY with type t = 'arr and type elt = 'elt) ->
cmp:('elt -> 'elt -> int) -> 'arr -> unit
(** [sort_generic (module M) cmp a] sorts the array [a], without allocating (eats stack space though).
(** [sort_generic (module M) ~cmp a] sorts the array [a], without allocating (eats stack space though).
Performance might be lower than {!Array.sort}.
@since 0.14 *)

View file

@ -1,21 +1,4 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(* fallback function *)
external make_float : int -> float array = "caml_make_float_vect"
include CCArray
(*$inject
module type A = module type of CCArray
module type AL = module type of CCArrayLabels
*)
(*$R
ignore (module CCArrayLabels : A)
*)
(*$R
ignore (module CCArray : AL)
*)

View file

@ -75,13 +75,15 @@ val fold_while : f:('a -> 'b -> 'a * [`Stop | `Continue]) -> init:'a -> 'b t ->
val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a t -> 'acc * 'b t
(** [fold_map ~f ~init a] is a [fold_left]-like function, but it also maps the
array to another array.
@since 2.1 *)
@since 1.2, but only
@since 2.1 with labels *)
val scan_left : f:('acc -> 'a -> 'acc) -> init:'acc -> 'a t -> 'acc t
(** [scan_left ~f ~init a] returns the array
[ [|~init; ~f ~init x0; ~f (~f ~init a.(0)) a.(1); |] ].
@since 2.1 *)
@since 1.2, but only
@since 2.1 with labels *)
val iter : f:('a -> unit) -> 'a t -> unit
(** [iter ~f a] applies function [~f] in turn to all elements of [a].
@ -106,10 +108,12 @@ val reverse_in_place : 'a t -> unit
(** [reverse_in_place a] reverses the array [a] in place. *)
val sorted : f:('a -> 'a -> int) -> 'a t -> 'a array
(* FIXME: better label this ~cmp ?? *)
(** [sorted ~f a] makes a copy of [a] and sorts it with [~f].
@since 1.0 *)
val sort_indices : f:('a -> 'a -> int) -> 'a t -> int array
(* FIXME: better label this ~cmp ?? *)
(** [sort_indices ~f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [sorted ~f a]
appears in [a]. [a] is not modified.
@ -119,6 +123,7 @@ val sort_indices : f:('a -> 'a -> int) -> 'a t -> int array
@since 1.0 *)
val sort_ranking : f:('a -> 'a -> int) -> 'a t -> int array
(* FIXME: better label this ~cmp ?? *)
(** [sort_ranking ~f a] returns a new array [b], with the same length as [a],
such that [b.(i)] is the index at which the [i]-th element of [a] appears
in [sorted ~f a]. [a] is not modified.
@ -133,39 +138,45 @@ val sort_ranking : f:('a -> 'a -> int) -> 'a t -> int array
val find_map : f:('a -> 'b option) -> 'a t -> 'b option
(** [find_map ~f a] returns [Some y] if there is an element [x] such
that [~f x = Some y]. Otherwise returns [None].
@since 2.1 *)
@since 1.3, but only
@since 2.1 with labels *)
val find : f:('a -> 'b option) -> 'a t -> 'b option
(** [find ~f a] is an alias to {!find_map}.
@deprecated since 1.3, use {!find_map} instead.
The version with labels is
@deprecated since 2.1, use {!find_map} instead. *)
val find_map_i : f:(int -> 'a -> 'b option) -> 'a t -> 'b option
(** [find_map_i ~f a] is like {!find_map}, but the index of the element is also passed
to the predicate function [~f].
@since 2.1 *)
@since 1.3, but only
@since 2.1 with labels *)
val findi : f:(int -> 'a -> 'b option) -> 'a t -> 'b option
(** [findi ~f a] is an alias to {!find_map_i}.
@since 0.3.4
@deprecated since 2.1, use {!find_map_i} instead. *)
@deprecated since 1.3, use {!find_map} instead.
The version with labels is
@deprecated since 2.1, use {!find_map} instead. *)
val find_idx : f:('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx ~f a] returns [Some (i,x)] where [x] is the [i]-th element of [a],
and [~f x] holds. Otherwise returns [None].
@since 0.3.4 *)
val lookup : cmp:'a ord -> key:'a -> 'a t -> int option
val lookup : cmp:('a ord [@keep_label]) -> key:'a -> 'a t -> int option
(** [lookup ~cmp ~key a] lookups the index of some key [~key] in a sorted array [a].
Undefined behavior if the array [a] is not sorted wrt [~cmp].
Complexity: [O(log (n))] (dichotomic search).
@return [None] if the key [~key] is not present, or
[Some i] ([i] the index of the key) otherwise. *)
val lookup_exn : cmp:'a ord -> key:'a -> 'a t -> int
val lookup_exn : cmp:('a ord [@keep_label]) -> key:'a -> 'a t -> int
(** [lookup_exn ~cmp ~key a] is like {!lookup}, but
@raise Not_found if the key [~key] is not present. *)
val bsearch : cmp:('a -> 'a -> int) -> key:'a -> 'a t ->
val bsearch : cmp:(('a -> 'a -> int) [@keep_label]) -> key:'a -> 'a t ->
[ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ]
(** [bsearch ~cmp ~key a] finds the index of the object [~key] in the array [a],
provided [a] is {b sorted} using [~cmp]. If the array is not sorted,
@ -270,8 +281,8 @@ val map2 : f:('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
and builds an array with the results returned by [~f]:
[[| ~f a.(0) b.(0); ...; ~f a.(length a - 1) b.(length b - 1)|]].
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *)
@raise Invalid_argument if [a] and [b] have distinct lengths.
@since 0.20 *)
val rev : 'a t -> 'a t
(** [rev a] copies the array [a] and reverses it in place.
@ -330,8 +341,7 @@ end
val sort_generic :
(module MONO_ARRAY with type t = 'arr and type elt = 'elt) ->
cmp:('elt -> 'elt -> int) -> 'arr -> unit
cmp:(('elt -> 'elt -> int) [@keep_label]) -> 'arr -> unit
(** [sort_generic (module M) ~cmp a] sorts the array [a], without allocating (eats stack space though).
Performance might be lower than {!Array.sort}.
@since 0.14 *)

View file

@ -1,3 +1,5 @@
(* AUTOGENERATED FROM CCArray_sliceLabels.mli *)
(* This file is free software, part of containers. See file "license" for more details. *)
@ -153,13 +155,13 @@ val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option
and [p x] holds. Otherwise returns [None].
@since 0.3.4 *)
val lookup : cmp:'a ord -> 'a -> 'a t -> int option
val lookup : cmp:('a ord) -> 'a -> 'a t -> int option
(** [lookup ~cmp x as] lookups the index [i] of some key [x] in the slice [as], provided [as] is
sorted using [cmp].
@return [None] if the key [x] is not present, or
[Some i] ([i] the index of the key) otherwise. *)
val lookup_exn : cmp:'a ord -> 'a -> 'a t -> int
val lookup_exn : cmp:('a ord) -> 'a -> 'a t -> int
(** [lookup_exn ~cmp x as] is like {!lookup}, but
@raise Not_found if the key [x] is not present. *)

View file

@ -0,0 +1,4 @@
(* This file is free software, part of containers. See file "license" for more details. *)
include CCArray_slice

View file

@ -0,0 +1,259 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Array Slice} *)
type 'a sequence = ('a -> unit) -> unit
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
type 'a gen = unit -> 'a option
type 'a equal = 'a -> 'a -> bool
type 'a ord = 'a -> 'a -> int
type 'a random_gen = Random.State.t -> 'a
type 'a printer = Format.formatter -> 'a -> unit
type 'a t
(** The type for an array slice, containing elements of type ['a] *)
val empty : 'a t
(** [empty] is the empty array slice. *)
val equal : 'a equal -> 'a t equal
(** [equal eq as1 as2] is [true] if the lengths of [as1] and [as2] are the same
and if the corresponding elements test equal using [eq]. *)
val compare : 'a ord -> 'a t ord
(** [compare cmp as1 as2] compares the two slices [as1] and [as2] using
the comparison function [cmp], element by element. *)
val get : 'a t -> int -> 'a
(** [get as n] returns the element number [n] of slice [as].
The first element has number 0.
The last element has number [length as - 1].
You can also write [as.(n)] instead of [get as n].
Raise [Invalid_argument "index out of bounds"]
if [n] is outside the range 0 to [(length as - 1)]. *)
val get_safe : 'a t -> int -> 'a option
(** [get_safe as i] returns [Some as.(i)] if [i] is a valid index.
@since 0.18 *)
val make : 'a array -> int -> len:(int [@keep_label]) -> 'a t
(** [make a i ~len] creates a slice from given offset [i] and length [len] of the given array [a].
@raise Invalid_argument if the slice isn't valid. *)
val of_slice : ('a array * int * int) -> 'a t
(** [of_slice (a, i, len)] makes a slice from a triple [(a, i, len)] where [a] is the array,
[i] the offset in [a], and [len] the number of elements of the slice.
@raise Invalid_argument if the slice isn't valid (See {!make}). *)
val to_slice : 'a t -> ('a array * int * int)
(** [to_slice as] converts the slice [as] into a triple [(a, i, len)] where [len] is the length of
the sub-array of [a] starting at offset [i]. *)
val to_list : 'a t -> 'a list
(** [to_list as] converts the slice [as] directly to a list.
@since 1.0 *)
val full : 'a array -> 'a t
(** [full a] creates a slice that covers the full array [a]. *)
val underlying : 'a t -> 'a array
(** [underlying as] returns the underlying array (shared). Modifying this array will modify
the slice [as]. *)
val copy : 'a t -> 'a array
(** [copy as] copies the slice [as] into a new array. *)
val sub : 'a t -> int -> int -> 'a t
(** [sub as i len] builds a new sub-slice that contains the given subrange specified
by the index [i] and the length [len]. *)
val set : 'a t -> int -> 'a -> unit
(** [set as n x] modifies the slice [as] in place, replacing
element number [n] with [x].
You can also write [as.(n) <- x] instead of [set as n x].
Raise [Invalid_argument "index out of bounds"]
if [n] is outside the range 0 to [length as - 1]. *)
val length : _ t -> int
(** [length as] returns the length (number of elements) of the given slice [as]. *)
val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** [fold f acc as] computes [f (... (f (f acc as.(0)) as.(1)) ...) as.(length as - 1)]. *)
val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a
(** [foldi f acc as] is just like {!fold} but it also passes in the index of each element
as the second argument to the folded function [f]. *)
val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a
(** [fold_while f acc as] folds left on slice [as] until a stop condition via [('a, `Stop)]
is indicated by the accumulator.
@since 0.8 *)
val iter : ('a -> unit) -> 'a t -> unit
(** [iter f as] applies function [f] in turn to all elements of [as].
It is equivalent to [f as.(0); f as.(1); ...; f as.(length as - 1); ()]. *)
val iteri : (int -> 'a -> unit) -> 'a t -> unit
(** [iteri f as] is like {!iter}, but the function [f] is applied with the index of the element
as first argument, and the element itself as second argument. *)
val blit : 'a t -> int -> 'a t -> int -> int -> unit
(** [blit as1 o1 as2 o2 len] copies [len] elements
from slice [as1], starting at element number [o1], to slice [as2],
starting at element number [o2]. It works correctly even if
[as1] and [as2] are the same slice, and the source and
destination chunks overlap.
Raise [Invalid_argument "CCArray_slice.blit"] if [o1] and [len] do not
designate a valid subarray of [as1], or if [o2] and [len] do not
designate a valid subarray of [as2]. *)
val reverse_in_place : 'a t -> unit
(** [reverse_in_place as] reverses the slice [as] in place. *)
val sorted : ('a -> 'a -> int) -> 'a t -> 'a array
(** [sorted cmp as] makes a copy of [as] and sorts it with [cmp].
@since 1.0 *)
val sort_indices : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_indices cmp as] returns a new array [b], with the same length as [as],
such that [b.(i)] is the index at which the [i]-th element of [sorted cmp as]
appears in [as]. [as] is not modified.
In other words, [map (fun i -> as.(i)) (sort_indices cmp as) = sorted cmp as].
[sort_indices] yields the inverse permutation of {!sort_ranking}.
@since 1.0 *)
val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array
(** [sort_ranking cmp as] returns a new array [b], with the same length as [as],
such that [b.(i)] is the index at which the [i]-th element of [as] appears
in [sorted cmp as]. [as] is not modified.
In other words, [map (fun i -> (sorted cmp as).(i)) (sort_ranking cmp as) = as].
[sort_ranking] yields the inverse permutation of {!sort_indices}.
In the absence of duplicate elements in [as], we also have
[lookup_exn as.(i) (sorted as) = (sorted_ranking as).(i)].
@since 1.0 *)
val find : ('a -> 'b option) -> 'a t -> 'b option
(** [find f as] returns [Some y] if there is an element [x] such
that [f x = Some y]. Otherwise returns [None]. *)
val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option
(** [findi f as] is like {!find}, but the index of the element is also passed
to the predicate function [f].
@since 0.3.4 *)
val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p as] returns [Some (i,x)] where [x] is the [i]-th element of [as],
and [p x] holds. Otherwise returns [None].
@since 0.3.4 *)
val lookup : cmp:(('a ord) [@keep_label]) -> 'a -> 'a t -> int option
(** [lookup ~cmp x as] lookups the index [i] of some key [x] in the slice [as], provided [as] is
sorted using [cmp].
@return [None] if the key [x] is not present, or
[Some i] ([i] the index of the key) otherwise. *)
val lookup_exn : cmp:(('a ord) [@keep_label]) -> 'a -> 'a t -> int
(** [lookup_exn ~cmp x as] is like {!lookup}, but
@raise Not_found if the key [x] is not present. *)
val bsearch : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a -> 'a t ->
[ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ]
(** [bsearch ~cmp x as] finds the index of the object [x] in the slice [as],
provided [as] is {b sorted} using [cmp]. If the slice is not sorted,
the result is not specified (may raise Invalid_argument).
Complexity: [O(log n)] where n is the length of the slice [as]
(dichotomic search).
@return
- [`At i] if [cmp as.(i) x = 0] (for some i).
- [`All_lower] if all elements of [as] are lower than [x].
- [`All_bigger] if all elements of [as] are bigger than [x].
- [`Just_after i] if [as.(i) < x < as.(i+1)].
- [`Empty] if the slice [as] is empty.
@raise Invalid_argument if the slice is found to be unsorted w.r.t [cmp].
@since 0.13 *)
val for_all : ('a -> bool) -> 'a t -> bool
(** [for_all p [|as1; ...; asn|]] checks if all elements of the slice
satisfy the predicate [p]. That is, it returns
[(p as1) && (p as2) && ... && (p asn)]. *)
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** [for_all2 p [|as1; ...; asn|] [|bs1; ...; bsn|]] is [true] if each pair of elements [asi bsi]
satisfies the predicate [p].
That is, it returns [(p as1 bs1) && (p as2 bs2) && ... && (p asn bsn)].
@raise Invalid_argument if slices have distinct lengths.
Allow different types.
@since 0.20 *)
val exists : ('a -> bool) -> 'a t -> bool
(** [exists p [|as1; ...; asn|]] is [true] if at least one element of
the slice satisfies the predicate [p]. That is, it returns
[(p as1) || (p as2) || ... || (p asn)]. *)
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
(** [exists2 p [|as1; ...; asn|] [|bs1; ...; bsn|]] is [true] if any pair of elements [asi bsi]
satisfies the predicate [p].
That is, it returns [(p as1 bs1) || (p as2 bs2) || ... || (p asn bsn)].
@raise Invalid_argument if slices have distinct lengths.
Allow different types.
@since 0.20 *)
val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
(** [fold2 f acc as bs] fold on two slices [as] and [bs] stepwise.
It computes [f (... (f acc as1 bs1)...) asn bsn].
@raise Invalid_argument if slices have distinct lengths.
@since 0.20 *)
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
(** [iter2 f as bs] iterates on the two slices [as] and [bs] stepwise.
It is equivalent to [f as0 bs0; ...; f as.(length as - 1) bs.(length bs - 1); ()].
@raise Invalid_argument if slices have distinct lengths.
@since 0.20 *)
val shuffle : 'a t -> unit
(** [shuffle as] randomly shuffles the slice [as], in place. *)
val shuffle_with : Random.State.t -> 'a t -> unit
(** [shuffle_with rs as] randomly shuffles the slice [as] (like {!shuffle}) but a specialized random
state [rs] is used to control the random numbers being produced during shuffling (for reproducibility). *)
val random_choose : 'a t -> 'a random_gen
(** [random_choose as rs] randomly chooses an element of [as].
@raise Not_found if the array/slice is empty. *)
val to_seq : 'a t -> 'a sequence
(** [to_seq as] returns a [sequence] of the elements of a slice [as].
The input slice [as] is shared with the sequence and modification of it will result
in modification of the sequence. *)
val to_gen : 'a t -> 'a gen
(** [to_gen as] returns a [gen] of the elements of a slice [as]. *)
val to_klist : 'a t -> 'a klist
(** [to_klist as] returns a [klist] of the elements of a slice [as]. *)
(** {2 IO} *)
val pp: ?sep:string -> 'a printer -> 'a t printer
(** [pp ~sep pp_item ppf as] formats the slice [as] on [ppf].
Each element is formatted with [pp_item] and elements are separated
by [sep] (defaults to ", "). *)
val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer
(** [pp_i ~sep pp_item ppf as] prints the slice [as] on [ppf].
The printing function [pp_item] is giving both index and element.
Elements are separated by [sep] (defaults to ", "). *)

View file

@ -1,3 +1,5 @@
(* AUTOGENERATED FROM CCEqualLabels.mli *)
(* This file is free software, part of containers. See file "license" for more details. *)

View file

@ -0,0 +1,4 @@
(* This file is free software, part of containers. See file "license" for more details. *)
include CCEqual

View file

@ -0,0 +1,44 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Equality Combinators} *)
(** @since 1.2 *)
type 'a t = 'a -> 'a -> bool
(** Equality function. Must be transitive, symmetric, and reflexive. *)
val poly : 'a t
(** Standard polymorphic equality. *)
val physical : 'a t
(** Standard physical equality.
@since 2.0 *)
val int : int t
val string : string t
val bool : bool t
val float : float t
val unit : unit t
val list : 'a t -> 'a list t
val array : 'a t -> 'a array t
val option : 'a t -> 'a option t
val pair : 'a t -> 'b t -> ('a * 'b) t
val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
val map : f:('a -> 'b) -> 'b t -> 'a t
(** [map f eq] is the equality function that, given objects [x] and [y],
projects [x] and [y] using [f] (e.g. using a record field) and then
compares those projections with [eq].
Example:
[map fst int] compares values of type [(int * 'a)] by their
first component. *)
module Infix : sig
val (>|=) : 'b t -> ('a -> 'b) -> 'a t
(** Infix equivalent of {!map}. *)
end
include module type of Infix

View file

@ -968,7 +968,7 @@ let find_idx p l = find_mapi (fun i x -> if p x then Some (i, x) else None) l
find_map (fun x -> if x=3 then Some "a" else None) [1;2;4;5] = None
*)
let remove ~eq ~x l =
let remove ~eq x l =
let rec remove' eq x acc l = match l with
| [] -> List.rev acc
| y :: tail when eq x y -> remove' eq x acc tail
@ -977,8 +977,8 @@ let remove ~eq ~x l =
remove' eq x [] l
(*$T
remove ~eq:CCInt.equal ~x:1 [2;1;3;3;2;1] = [2;3;3;2]
remove ~eq:CCInt.equal ~x:10 [1;2;3] = [1;2;3]
remove ~eq:CCInt.equal 1 [2;1;3;3;2;1] = [2;3;3;2]
remove ~eq:CCInt.equal 10 [1;2;3] = [1;2;3]
*)
let filter_map f l =
@ -1483,7 +1483,7 @@ module Assoc = struct
not (Assoc.mem ~eq:CCInt.equal 4 [1,"1"; 2,"2"; 3, "3"])
*)
let update ~eq ~f x l =
let update ~eq f x l =
search_set eq [] l x
~f:(fun x opt_y rest ->
match f opt_y with
@ -1492,13 +1492,13 @@ module Assoc = struct
(*$=
[1,"1"; 2,"22"] \
(Assoc.update ~eq:CCInt.equal \
~f:(function Some "2" -> Some "22" | _ -> assert false) 2 [1,"1"; 2,"2"] |> lsort)
(function Some "2" -> Some "22" | _ -> assert false) 2 [1,"1"; 2,"2"] |> lsort)
[1,"1"; 3,"3"] \
(Assoc.update ~eq:CCInt.equal \
~f:(function Some "2" -> None | _ -> assert false) 2 [1,"1"; 2,"2"; 3,"3"] |> lsort)
(function Some "2" -> None | _ -> assert false) 2 [1,"1"; 2,"2"; 3,"3"] |> lsort)
[1,"1"; 2,"2"; 3,"3"] \
(Assoc.update ~eq:CCInt.equal \
~f:(function None -> Some "3" | _ -> assert false) 3 [1,"1"; 2,"2"] |> lsort)
(function None -> Some "3" | _ -> assert false) 3 [1,"1"; 2,"2"] |> lsort)
*)
let remove ~eq x l =

View file

@ -1,3 +1,5 @@
(* AUTOGENERATED FROM CCListLabels.mli *)
(* This file is free software, part of containers. See file "license" for more details. *)
@ -61,14 +63,15 @@ val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a
@since 0.8 *)
val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a list -> 'acc * 'b list
(** [fold_map f acc l] is a [fold_left]-like function, but it also maps the
(** [fold_map f init l] is a [fold_left]-like function, but it also maps the
list to another list.
@since 0.14 *)
val scan_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a list -> 'acc list
(** [scan_left f acc l] returns the list [[acc; f acc x0; f (f acc x0) x1; ...]]
(** [scan_left f init l] returns the list [[init; f init x0; f (f init x0) x1; ...]]
where [x0], [x1], etc. are the elements of [l].
@since 1.2 *)
@since 1.2, but only
@since 2.2 with labels *)
val fold_map2 : ('acc -> 'a -> 'b -> 'acc * 'c) -> 'acc -> 'a list -> 'b list -> 'acc * 'c list
(** [fold_map2] is to [fold_map] what [List.map2] is to [List.map].
@ -76,7 +79,7 @@ val fold_map2 : ('acc -> 'a -> 'b -> 'acc * 'c) -> 'acc -> 'a list -> 'b list ->
@since 0.16 *)
val fold_filter_map : ('acc -> 'a -> 'acc * 'b option) -> 'acc -> 'a list -> 'acc * 'b list
(** [fold_filter_map f acc l] is a [fold_left]-like function, but also
(** [fold_filter_map f init l] is a [fold_left]-like function, but also
generates a list of output in a way similar to {!filter_map}.
@since 0.17 *)
@ -87,12 +90,11 @@ val fold_flat_map : ('acc -> 'a -> 'acc * 'b list) -> 'acc -> 'a list -> 'acc *
val count : ('a -> bool) -> 'a list -> int
(** [count p l] counts how many elements of [l] satisfy predicate [p].
@since 1.5 *)
@since 1.5, but only
@since 2.2 with labels *)
val count_true_false : ('a -> bool) -> 'a list -> int * int
(** [let ok_count, ko_count = count_true_false p l in ...]
count_true_false how many elements of [l] satisfy (resp. violate) predicate [p].
@since NEXT_RELEASE *)
(** @since NEXT_RELEASE *)
val init : int -> (int -> 'a) -> 'a t
(** [init len f] is [f 0; f 1; ...; f (len-1)].
@ -105,31 +107,37 @@ val combine : 'a list -> 'b list -> ('a * 'b) list
[combine [a1; ...; an] [b1; ...; bn]] is
[[(a1,b1); ...; (an,bn)]].
@raise Invalid_argument if the lists have distinct lengths.
@since 1.2 *)
@since 1.2, but only
@since 2.2 with labels *)
val combine_gen : 'a list -> 'b list -> ('a * 'b) gen
(** Lazy version of {!combine}.
Unlike {!combine}, it does not fail if the lists have different
lengths;
instead, the output has as many pairs as the smallest input list.
@since 1.2 *)
@since 1.2, but only
@since 2.2 with labels *)
val split : ('a * 'b) t -> 'a t * 'b t
(** A tail-recursive version of {!List.split}.
Transform a list of pairs into a pair of lists:
[split [(a1,b1); ...; (an,bn)]] is [([a1; ...; an], [b1; ...; bn])]. *)
[split [(a1,b1); ...; (an,bn)]] is [([a1; ...; an], [b1; ...; bn])].
@since 1.2, but only
@since 2.2 with labels *)
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
val compare_lengths : 'a t -> 'b t -> int
(** Equivalent to [compare (length l1) (length l2)] but more efficient.
Compare the lengths of two lists.
@since 1.5 *)
@since 1.5, but only
@since 2.2 with labels *)
val compare_length_with : 'a t -> int -> int
(** Equivalent to [compare (length l) x] but more efficient.
Compare the length of a list to an integer.
@since 1.5 *)
@since 1.5, but only
@since 2.2 with labels *)
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
@ -158,14 +166,16 @@ val cartesian_product : 'a t t -> 'a t t
[[1;3;4;5;6];[2;3;4;5;6]];;
]}
invariant: [cartesian_product l = map_product id l].
@since 1.2 *)
@since 1.2, but only
@since 2.2 with labels *)
val map_product_l : ('a -> 'b list) -> 'a list -> 'b list list
(** [map_product_l f l] maps each element of [l] to a list of
objects of type ['b] using [f].
We obtain [[l1;l2;...;ln]] where [length l=n] and [li : 'b list].
Then, it returns all the ways of picking exactly one element per [li].
@since 1.2 *)
@since 1.2, but only
@since 2.2 with labels *)
val diagonal : 'a t -> ('a * 'a) t
(** All pairs of distinct positions of the list. [list_diagonal l] will
@ -263,16 +273,21 @@ val sublists_of_len :
If [last = CCOpt.return], it will simply keep the last group.
By default, [last = fun _ -> None], i.e. the last group is dropped if shorter than [n].
@raise Invalid_argument if [offset <= 0] or [n <= 0].
@since 1.0 *)
See {!CCList.sublists_of_len} for more details.
@since 1.0, but only
@since 1.5 with labels *)
val intersperse : 'a -> 'a list -> 'a list
(** Insert the first argument between every element of the list.
@since 2.1 *)
@since 2.1, but only
@since 2.2 with labels *)
val interleave : 'a list -> 'a list -> 'a list
(** [interleave [x1…xn] [y1…ym]] is [x1,y1,x2,y2,…] and finishes with
the suffix of the longest list.
@since 2.1 *)
@since 2.1, but only
@since 2.2 with labels *)
val pure : 'a -> 'a t
(** [pure] is [return]. *)
@ -281,7 +296,7 @@ val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
(** [funs <*> l] is [product (fun f x -> f x) funs l]. *)
val (<$>) : ('a -> 'b) -> 'a t -> 'b t
(** [(<$>)] = [map]. *)
(** [(<$>)] is [map]. *)
val return : 'a -> 'a t
(** [return x] is [x]. *)
@ -314,7 +329,8 @@ val drop_while : ('a -> bool) -> 'a t -> 'a t
val take_drop_while : ('a -> bool) -> 'a t -> 'a t * 'a t
(** [take_drop_while p l] = [take_while p l, drop_while p l].
@since 1.2 *)
@since 1.2, but only
@since 2.2 with labels *)
val last : int -> 'a t -> 'a t
(** [last n l] takes the last [n] elements of [l] (or less if
@ -339,7 +355,8 @@ val find_pred : ('a -> bool) -> 'a t -> 'a option
val find_opt : ('a -> bool) -> 'a t -> 'a option
(** Safe version of {!find}.
@since 1.5 *)
@since 1.5, but only
@since 2.2 with labels *)
val find_pred_exn : ('a -> bool) -> 'a t -> 'a
(** Unsafe version of {!find_pred}.
@ -360,8 +377,9 @@ val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l],
and [p x] holds. Otherwise returns [None]. *)
val remove : eq:('a -> 'a -> bool) -> x:'a -> 'a t -> 'a t
(** [remove ~x l] removes every instance of [x] from [l]. Tail-recursive.
val remove : eq:('a -> 'a -> bool) -> 'a -> 'a t -> 'a t
(* FIXME: the original CCList.mli uses ~x instead of ~key !! *)
(** [remove key l] removes every instance of [key] from [l]. Tail-recursive.
@param eq equality function.
@since 0.11 *)
@ -373,21 +391,25 @@ val filter_map : ('a -> 'b option) -> 'a t -> 'b t
val keep_some : 'a option t -> 'a t
(** [keep_some l] retains only elements of the form [Some x].
Like [filter_map CCFun.id].
@since 1.3 *)
@since 1.3, but only
@since 2.2 with labels *)
val keep_ok : ('a, _) Result.result t -> 'a t
(** [keep_ok l] retains only elements of the form [Ok x].
@since 1.3 *)
@since 1.3, but only
@since 2.2 with labels *)
val all_some : 'a option t -> 'a t option
(** [all_some l] returns [Some l'] if all elements of [l] are of the form [Some x],
or [None] otherwise.
@since 1.3 *)
@since 1.3, but only
@since 2.2 with labels *)
val all_ok : ('a, 'err) Result.result t -> ('a t, 'err) Result.result
(** [all_ok l] returns [Ok l'] if all elements of [l] are of the form [Ok x],
or [Error e] otherwise (with the first error met).
@since 1.3 *)
@since 1.3, but only
@since 2.2 with labels *)
val sorted_merge : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
(** Merge elements from both sorted list. *)
@ -439,7 +461,8 @@ val iteri : (int -> 'a -> unit) -> 'a t -> unit
val iteri2 : (int -> 'a -> 'b -> unit) -> 'a t -> 'b t -> unit
(** Iter on two lists.
@raise Invalid_argument when lists do not have the same length.
@since 2.0 *)
@since 2.0, but only
@since 2.2 with labels *)
val foldi : ('b -> int -> 'a -> 'b) -> 'b -> 'a t -> 'b
(** Like [fold] but it also passes in the index of each element to the folded function. Tail-recursive. *)
@ -447,7 +470,8 @@ val foldi : ('b -> int -> 'a -> 'b) -> 'b -> 'a t -> 'b
val foldi2 : ('c -> int -> 'a -> 'b -> 'c) -> 'c -> 'a t -> 'b t -> 'c
(** Fold on two lists, with index.
@raise Invalid_argument when lists do not have the same length.
@since 2.0 *)
@since 2.0, but only
@since 2.2 with labels *)
val get_at_idx : int -> 'a t -> 'a option
(** Get by index in the list.
@ -457,7 +481,8 @@ val get_at_idx : int -> 'a t -> 'a option
val nth_opt : 'a t -> int -> 'a option
(** Safe version of {!nth}.
@raise Invalid_argument if the int is negative.
@since 1.5 *)
@since 1.5, but only
@since 2.2 with labels *)
val get_at_idx_exn : int -> 'a t -> 'a
(** Get the i-th element, or
@ -564,8 +589,9 @@ module Assoc : sig
@since 0.16 *)
val update :
eq:('a->'a->bool) -> f:('b option -> 'b option) -> 'a -> ('a,'b) t -> ('a,'b) t
(** [update k ~f l] updates [l] on the key [k], by calling [f (get l k)]
eq:('a->'a->bool) -> ('b option -> 'b option) -> 'a -> ('a,'b) t -> ('a,'b) t
(* FIXME: the original no labels mli kept the ~f label ! *)
(** [update k f l] updates [l] on the key [k], by calling [f (get l k)]
and removing [k] if it returns [None], mapping [k] to [v'] if it
returns [Some v'].
@since 0.16 *)
@ -581,11 +607,13 @@ val assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> 'b
val assoc_opt : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> 'b option
(** Like [Assoc.get].
@since 1.5 *)
@since 1.5, but only
@since 2.0 with labels *)
val assq_opt : 'a -> ('a * 'b) t -> 'b option
(** Safe version of {!assq}.
@since 1.5 *)
@since 1.5, but only
@since 2.0 with labels *)
val mem_assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * _) t -> bool
(** Like [Assoc.mem].

View file

@ -2,17 +2,3 @@
(* This file is free software, part of containers. See file "license" for more details. *)
include CCList
(*$inject
module type L = module type of CCList
module type LL = module type of CCListLabels
*)
(*$R
ignore (module CCListLabels : L)
*)
(*$R
ignore (module CCList : LL)
*)

View file

@ -66,7 +66,10 @@ val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a list -> 'acc * 'b
@since 0.14 *)
val scan_left : f:('acc -> 'a -> 'acc) -> init:'acc -> 'a list -> 'acc list
(** @since 2.2 *)
(** [scan_left ~f ~init l] returns the list [[init; f init x0; f (f init x0) x1; ...]]
where [x0], [x1], etc. are the elements of [l].
@since 1.2, but only
@since 2.2 with labels *)
val fold_map2 : f:('acc -> 'a -> 'b -> 'acc * 'c) -> init:'acc -> 'a list -> 'b list -> 'acc * 'c list
(** [fold_map2] is to [fold_map] what [List.map2] is to [List.map].
@ -84,7 +87,9 @@ val fold_flat_map : f:('acc -> 'a -> 'acc * 'b list) -> init:'acc -> 'a list ->
@since 0.14 *)
val count : f:('a -> bool) -> 'a list -> int
(** @since 2.2 *)
(** [count p l] counts how many elements of [l] satisfy predicate [p].
@since 1.5, but only
@since 2.2 with labels *)
val count_true_false : f:('a -> bool) -> 'a list -> int * int
(** @since NEXT_RELEASE *)
@ -95,21 +100,42 @@ val init : int -> f:(int -> 'a) -> 'a t
@since 0.6 *)
val combine : 'a list -> 'b list -> ('a * 'b) list
(** @since 2.2 *)
(** Like {!List.combine} but tail-recursive.
Transform a pair of lists into a list of pairs:
[combine [a1; ...; an] [b1; ...; bn]] is
[[(a1,b1); ...; (an,bn)]].
@raise Invalid_argument if the lists have distinct lengths.
@since 1.2, but only
@since 2.2 with labels *)
val combine_gen : 'a list -> 'b list -> ('a * 'b) gen
(** @since 2.2 *)
(** Lazy version of {!combine}.
Unlike {!combine}, it does not fail if the lists have different
lengths;
instead, the output has as many pairs as the smallest input list.
@since 1.2, but only
@since 2.2 with labels *)
val split : ('a * 'b) t -> 'a t * 'b t
(** @since 2.2 *)
(** A tail-recursive version of {!List.split}.
Transform a list of pairs into a pair of lists:
[split [(a1,b1); ...; (an,bn)]] is [([a1; ...; an], [b1; ...; bn])].
@since 1.2, but only
@since 2.2 with labels *)
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
val compare_lengths : 'a t -> 'b t -> int
(** @since 2.2 *)
(** Equivalent to [compare (length l1) (length l2)] but more efficient.
Compare the lengths of two lists.
@since 1.5, but only
@since 2.2 with labels *)
val compare_length_with : 'a t -> int -> int
(** @since 2.2 *)
(** Equivalent to [compare (length l) x] but more efficient.
Compare the length of a list to an integer.
@since 1.5, but only
@since 2.2 with labels *)
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
@ -126,10 +152,28 @@ val fold_product : f:('c -> 'a -> 'b -> 'c) -> init:'c -> 'a t -> 'b t -> 'c
(** Fold on the cartesian product. *)
val cartesian_product : 'a t t -> 'a t t
(** @since 2.2 *)
(** Produce the cartesian product of this list of lists,
by returning all the ways of picking one element per sublist.
{b NOTE} the order of the returned list is unspecified.
For example:
{[
# cartesian_product [[1;2];[3];[4;5;6]] |> sort =
[[1;3;4];[1;3;5];[1;3;6];[2;3;4];[2;3;5];[2;3;6]];;
# cartesian_product [[1;2];[];[4;5;6]] = [];;
# cartesian_product [[1;2];[3];[4];[5];[6]] |> sort =
[[1;3;4;5;6];[2;3;4;5;6]];;
]}
invariant: [cartesian_product l = map_product id l].
@since 1.2, but only
@since 2.2 with labels *)
val map_product_l : f:('a -> 'b list) -> 'a list -> 'b list list
(** @since 2.2 *)
(** [map_product_l f l] maps each element of [l] to a list of
objects of type ['b] using [f].
We obtain [[l1;l2;...;ln]] where [length l=n] and [li : 'b list].
Then, it returns all the ways of picking exactly one element per [li].
@since 1.2, but only
@since 2.2 with labels *)
val diagonal : 'a t -> ('a * 'a) t
(** All pairs of distinct positions of the list. [list_diagonal l] will
@ -149,7 +193,7 @@ val group_by : ?hash:('a -> int) -> ?eq:('a -> 'a -> bool) ->
precondition: for any [x] and [y], if [eq x y] then [hash x=hash y] must hold.
@since 2.3 *)
val join : join_row:('a -> 'b -> 'c option) -> 'a t -> 'b t -> 'c t
val join : join_row:(('a -> 'b -> 'c option) [@keep_label]) -> 'a t -> 'b t -> 'c t
(** [join ~join_row a b] combines every element of [a] with every
element of [b] using [join_row]. If [join_row] returns None, then
the two elements do not combine. Assume that [b] allows for multiple
@ -158,7 +202,7 @@ val join : join_row:('a -> 'b -> 'c option) -> 'a t -> 'b t -> 'c t
val join_by : ?eq:('key -> 'key -> bool) -> ?hash:('key -> int) ->
('a -> 'key) -> ('b -> 'key) ->
merge:('key -> 'a -> 'b -> 'c option) ->
merge:(('key -> 'a -> 'b -> 'c option) [@keep_label]) ->
'a t ->
'b t ->
'c t
@ -173,7 +217,7 @@ val join_by : ?eq:('key -> 'key -> bool) -> ?hash:('key -> int) ->
val join_all_by : ?eq:('key -> 'key -> bool) -> ?hash:('key -> int) ->
('a -> 'key) -> ('b -> 'key) ->
merge:('key -> 'a list -> 'b list -> 'c option) ->
merge:(('key -> 'a list -> 'b list -> 'c option) [@keep_label]) ->
'a t ->
'b t ->
'c t
@ -201,7 +245,6 @@ val group_join_by : ?eq:('a -> 'a -> bool) -> ?hash:('a -> int) ->
precondition: for any [x] and [y], if [eq x y] then [hash x=hash y] must hold.
@since 2.3 *)
val sublists_of_len :
?last:('a list -> 'a list option) ->
?offset:int ->
@ -212,24 +255,43 @@ val sublists_of_len :
By default, these sub-lists are non overlapping:
[sublists_of_len 2 [1;2;3;4;5;6]] returns [[1;2]; [3;4]; [5;6]].
Examples:
- [sublists_of_len 2 [1;2;3;4;5;6] = [[1;2]; [3;4]; [5;6]]].
- [sublists_of_len 2 ~offset:3 [1;2;3;4;5;6] = [1;2];[4;5]].
- [sublists_of_len 3 ~last:CCOpt.return [1;2;3;4] = [1;2;3];[4]].
- [sublists_of_len 2 [1;2;3;4;5] = [[1;2]; [3;4]]].
@param offset the number of elements skipped between two consecutive
sub-lists. By default it is [n]. If [offset < n], the sub-lists
will overlap; if [offset > n], some elements will not appear at all.
@param last if provided and the last group of elements [g] is such
that [length g < n], [last g] is called. If [last g = Some g'],
[g'] is appended; otherwise [g] is dropped.
If [last = CCOpt.return], it will simply keep the last group.
By default, [last = fun _ -> None], i.e. the last group is dropped if shorter than [n].
@raise Invalid_argument if [offset <= 0] or [n <= 0].
See {!CCList.sublists_of_len} for more details.
@since 1.5 *)
@since 1.0, but only
@since 1.5 with labels *)
val intersperse : x:'a -> 'a list -> 'a list
(** Insert the first argument between every element of the list.
@since 2.2 *)
@since 2.1, but only
@since 2.2 with labels *)
val interleave : 'a list -> 'a list -> 'a list
(** [interleave [x1…xn] [y1…ym]] is [x1,y1,x2,y2,…] and finishes with
the suffix of the longest list.
@since 2.2 *)
@since 2.1, but only
@since 2.2 with labels *)
val pure : 'a -> 'a t
(** [pure] is [return]. *)
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
(** [funs <*> l] is [product fun f x -> f x) funs l]. *)
(** [funs <*> l] is [product (fun f x -> f x) funs l]. *)
val (<$>) : ('a -> 'b) -> 'a t -> 'b t
(** [(<$>)] is [map]. *)
@ -264,7 +326,9 @@ val drop_while : f:('a -> bool) -> 'a t -> 'a t
@since 0.13 *)
val take_drop_while : f:('a -> bool) -> 'a t -> 'a t * 'a t
(** @since 2.2 *)
(** [take_drop_while p l] = [take_while p l, drop_while p l].
@since 1.2, but only
@since 2.2 with labels *)
val last : int -> 'a t -> 'a t
(** [last n l] takes the last [n] elements of [l] (or less if
@ -288,7 +352,9 @@ val find_pred : f:('a -> bool) -> 'a t -> 'a option
@since 0.11 *)
val find_opt : f:('a -> bool) -> 'a t -> 'a option
(** @since 2.2 *)
(** Safe version of {!find}.
@since 1.5, but only
@since 2.2 with labels *)
val find_pred_exn : f:('a -> bool) -> 'a t -> 'a
(** Unsafe version of {!find_pred}.
@ -309,7 +375,8 @@ val find_idx : f:('a -> bool) -> 'a t -> (int * 'a) option
(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l],
and [p x] holds. Otherwise returns [None]. *)
val remove : eq:('a -> 'a -> bool) -> key:'a -> 'a t -> 'a t
val remove : eq:(('a -> 'a -> bool) [@keep_label]) -> key:'a -> 'a t -> 'a t
(* FIXME: the original CCList.mli uses ~x instead of ~key !! *)
(** [remove ~key l] removes every instance of [key] from [l]. Tail-recursive.
@param eq equality function.
@since 0.11 *)
@ -320,48 +387,59 @@ val filter_map : f:('a -> 'b option) -> 'a t -> 'b t
Map and remove elements at the same time. *)
val keep_some : 'a option t -> 'a t
(** @since 2.2 *)
(** [keep_some l] retains only elements of the form [Some x].
Like [filter_map CCFun.id].
@since 1.3, but only
@since 2.2 with labels *)
val keep_ok : ('a, _) Result.result t -> 'a t
(** @since 2.2 *)
(** [keep_ok l] retains only elements of the form [Ok x].
@since 1.3, but only
@since 2.2 with labels *)
val all_some : 'a option t -> 'a t option
(** @since 2.2 *)
(** [all_some l] returns [Some l'] if all elements of [l] are of the form [Some x],
or [None] otherwise.
@since 1.3, but only
@since 2.2 with labels *)
val all_ok : ('a, 'err) Result.result t -> ('a t, 'err) Result.result
(** @since 2.2 *)
(** [all_ok l] returns [Ok l'] if all elements of [l] are of the form [Ok x],
or [Error e] otherwise (with the first error met).
@since 1.3, but only
@since 2.2 with labels *)
val sorted_merge : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
(** Merges elements from both sorted list. *)
val sorted_merge : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a list -> 'a list
(** Merge elements from both sorted list. *)
val sort_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a list
val sort_uniq : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a list
(** Sort the list and remove duplicate elements. *)
val sorted_merge_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
val sorted_merge_uniq : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a list -> 'a list
(** [sorted_merge_uniq l1 l2] merges the sorted lists [l1] and [l2] and
removes duplicates.
@since 0.10 *)
val is_sorted : cmp:('a -> 'a -> int) -> 'a list -> bool
val is_sorted : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> bool
(** [is_sorted l] returns [true] iff [l] is sorted (according to given order).
@param cmp the comparison function (default [Pervasives.compare]).
@since 0.17 *)
val sorted_insert : cmp:('a -> 'a -> int) -> ?uniq:bool -> 'a -> 'a list -> 'a list
val sorted_insert : cmp:(('a -> 'a -> int) [@keep_label]) -> ?uniq:bool -> 'a -> 'a list -> 'a list
(** [sorted_insert x l] inserts [x] into [l] such that, if [l] was sorted,
then [sorted_insert x l] is sorted too.
@param uniq if true and [x] is already in sorted position in [l], then
[x] is not duplicated. Default [false] ([x] will be inserted in any case).
@since 0.17 *)
val uniq_succ : eq:('a -> 'a -> bool) -> 'a list -> 'a list
val uniq_succ : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a list -> 'a list
(** [uniq_succ l] removes duplicate elements that occur one next to the other.
Examples:
[uniq_succ [1;2;1] = [1;2;1]].
[uniq_succ [1;1;2] = [1;2]].
@since 0.10 *)
val group_succ : eq:('a -> 'a -> bool) -> 'a list -> 'a list list
val group_succ : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a list -> 'a list list
(** [group_succ ~eq l] groups together consecutive elements that are equal
according to [eq].
@since 0.11 *)
@ -379,13 +457,19 @@ val iteri : f:(int -> 'a -> unit) -> 'a t -> unit
itself as second argument. *)
val iteri2 : f:(int -> 'a -> 'b -> unit) -> 'a t -> 'b t -> unit
(** @since 2.2 *)
(** Iter on two lists.
@raise Invalid_argument when lists do not have the same length.
@since 2.0, but only
@since 2.2 with labels *)
val foldi : f:('b -> int -> 'a -> 'b) -> init:'b -> 'a t -> 'b
(** Like [fold] but it also passes in the index of each element to the folded function. Tail-recursive. *)
val foldi2 : f:('c -> int -> 'a -> 'b -> 'c) -> init:'c -> 'a t -> 'b t -> 'c
(** @since 2.2 *)
(** Fold on two lists, with index.
@raise Invalid_argument when lists do not have the same length.
@since 2.0, but only
@since 2.2 with labels *)
val get_at_idx : int -> 'a t -> 'a option
(** Get by index in the list.
@ -393,7 +477,10 @@ val get_at_idx : int -> 'a t -> 'a option
of the list. *)
val nth_opt : 'a t -> int -> 'a option
(** @since 2.2 *)
(** Safe version of {!nth}.
@raise Invalid_argument if the int is negative.
@since 1.5, but only
@since 2.2 with labels *)
val get_at_idx_exn : int -> 'a t -> 'a
(** Get the i-th element, or
@ -424,38 +511,38 @@ val remove_at_idx : int -> 'a t -> 'a t
Those operations maintain the invariant that the list does not
contain duplicates (if it already satisfies it). *)
val add_nodup : eq:('a -> 'a -> bool) -> 'a -> 'a t -> 'a t
val add_nodup : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> 'a t -> 'a t
(** [add_nodup x set] adds [x] to [set] if it was not already present. Linear time.
@since 0.11 *)
val remove_one : eq:('a -> 'a -> bool) -> 'a -> 'a t -> 'a t
val remove_one : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> 'a t -> 'a t
(** [remove_one x set] removes one occurrence of [x] from [set]. Linear time.
@since 0.11 *)
val mem : eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool
val mem : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> 'a t -> bool
(** Membership to the list. Linear time. *)
val subset : eq:('a -> 'a -> bool) -> 'a t -> 'a t -> bool
val subset : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a t -> 'a t -> bool
(** Test for inclusion. *)
val uniq : eq:('a -> 'a -> bool) -> 'a t -> 'a t
val uniq : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a t -> 'a t
(** Remove duplicates w.r.t the equality predicate.
Complexity is quadratic in the length of the list, but the order
of elements is preserved. If you wish for a faster de-duplication
but do not care about the order, use {!sort_uniq}. *)
val union : eq:('a -> 'a -> bool) -> 'a t -> 'a t -> 'a t
val union : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a t -> 'a t -> 'a t
(** List union. Complexity is product of length of inputs. *)
val inter : eq:('a -> 'a -> bool) -> 'a t -> 'a t -> 'a t
val inter : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a t -> 'a t -> 'a t
(** List intersection. Complexity is product of length of inputs. *)
(** {2 Other Constructors} *)
val range_by : step:int -> int -> int -> int t
val range_by : step:(int [@keep_label]) -> int -> int -> int t
(** [range_by ~step i j] iterates on integers from [i] to [j] included,
where the difference between successive elements is [step].
use a negative [step] for a decreasing list.
Use a negative [step] for a decreasing list.
@raise Invalid_argument if [step=0].
@since 0.18 *)
@ -485,49 +572,52 @@ val repeat : int -> 'a t -> 'a t
module Assoc : sig
type ('a, 'b) t = ('a*'b) list
val get : eq:('a->'a->bool) -> 'a -> ('a,'b) t -> 'b option
val get : eq:(('a->'a->bool) [@keep_label]) -> 'a -> ('a,'b) t -> 'b option
(** Find the element. *)
val get_exn : eq:('a->'a->bool) -> 'a -> ('a,'b) t -> 'b
val get_exn : eq:(('a->'a->bool) [@keep_label]) -> 'a -> ('a,'b) t -> 'b
(** Like [get], but unsafe.
@raise Not_found if the element is not present. *)
val set : eq:('a->'a->bool) -> 'a -> 'b -> ('a,'b) t -> ('a,'b) t
val set : eq:(('a->'a->bool) [@keep_label]) -> 'a -> 'b -> ('a,'b) t -> ('a,'b) t
(** Add the binding into the list (erase it if already present). *)
val mem : eq:('a->'a->bool) -> 'a -> ('a,_) t -> bool
val mem : eq:(('a->'a->bool) [@keep_label]) -> 'a -> ('a,_) t -> bool
(** [mem x l] returns [true] iff [x] is a key in [l].
@since 0.16 *)
val update :
eq:('a->'a->bool) -> f:('b option -> 'b option) -> 'a -> ('a,'b) t -> ('a,'b) t
eq:(('a->'a->bool) [@keep_label]) -> f:('b option -> 'b option) -> 'a -> ('a,'b) t -> ('a,'b) t
(* FIXME: the original no labels mli kept the ~f label ! *)
(** [update k ~f l] updates [l] on the key [k], by calling [f (get l k)]
and removing [k] if it returns [None], mapping [k] to [v'] if it
returns [Some v'].
@since 0.16 *)
val remove : eq:('a->'a->bool) -> 'a -> ('a,'b) t -> ('a,'b) t
val remove : eq:(('a->'a->bool) [@keep_label]) -> 'a -> ('a,'b) t -> ('a,'b) t
(** [remove x l] removes the first occurrence of [k] from [l].
@since 0.17 *)
end
val assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> 'b
val assoc : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> ('a * 'b) t -> 'b
(** Like [Assoc.get_exn].
@since 2.0 *)
val assoc_opt : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> 'b option
val assoc_opt : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> ('a * 'b) t -> 'b option
(** Like [Assoc.get].
@since 2.0 *)
@since 1.5, but only
@since 2.0 with labels *)
val assq_opt : 'a -> ('a * 'b) t -> 'b option
(** Safe version of {!assq}.
@since 2.0 *)
@since 1.5, but only
@since 2.0 with labels *)
val mem_assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * _) t -> bool
val mem_assoc : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> ('a * _) t -> bool
(** Like [Assoc.mem].
@since 2.0 *)
val remove_assoc : eq:('a -> 'a -> bool) -> 'a -> ('a * 'b) t -> ('a * 'b) t
val remove_assoc : eq:(('a -> 'a -> bool) [@keep_label]) -> 'a -> ('a * 'b) t -> ('a * 'b) t
(** Like [Assoc.remove].
@since 2.0 *)
@ -567,7 +657,6 @@ module type MONAD = sig
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
(** Monadic [bind]. *)
end
module Traverse(M : MONAD) : sig
@ -599,19 +688,22 @@ val to_seq : 'a t -> 'a sequence
(** Return a [sequence] of the elements of the list. *)
val of_seq : 'a sequence -> 'a t
(** Build a list from a given [sequence]. *)
(** Build a list from a given [sequence].
In the result, elements appear in the same order as they did in the source [sequence]. *)
val to_gen : 'a t -> 'a gen
(** Return a [gen] of the elements of the list. *)
val of_gen : 'a gen -> 'a t
(** Build a list from a given [gen]. *)
(** Build a list from a given [gen].
In the result, elements appear in the same order as they did in the source [gen]. *)
val to_klist : 'a t -> 'a klist
(** Return a [klist] of the elements of the list. *)
val of_klist : 'a klist -> 'a t
(** Build a list from a given [klist]. *)
(** Build a list from a given [klist].
In the result, elements appear in the same order as they did in the source [klist]. *)
(** {2 Infix Operators}
It is convenient to {!open CCList.Infix} to access the infix operators

View file

@ -1,3 +1,5 @@
(* AUTOGENERATED FROM CCStringLabels.mli *)
(* This file is free software, part of containers. See file "license" for more details. *)
@ -142,7 +144,7 @@ val replace : ?which:[`Left|`Right|`All] -> sub:string -> by:string -> string ->
@raise Invalid_argument if [sub = ""].
@since 0.14 *)
val is_sub : sub:string -> int -> string -> int -> len:int -> bool
val is_sub : sub:string -> int -> string -> int -> sub_len:int -> bool
(** [is_sub ~sub i s j ~len] returns [true] iff the substring of
[sub] starting at position [i] and of length [len] is a substring
of [s] starting at position [j]. *)
@ -333,12 +335,12 @@ module Find : sig
val rcompile : string -> [ `Reverse ] pattern
val find : ?start:int -> pattern:[`Direct] pattern -> string -> int
val find : ?start:int -> pattern:([`Direct] pattern) -> string -> int
(** Search for [pattern] in the string, left-to-right.
@return the offset of the first match, -1 otherwise.
@param start offset in string at which we start. *)
val rfind : ?start:int -> pattern:[`Reverse] pattern -> string -> int
val rfind : ?start:int -> pattern:([`Reverse] pattern) -> string -> int
(** Search for [pattern] in the string, right-to-left.
@return the offset of the start of the first match from the right, -1 otherwise.
@param start right-offset in string at which we start. *)

View file

@ -0,0 +1,4 @@
(* This file is free software, part of containers. See file "license" for more details. *)
include CCString

472
src/core/CCStringLabels.mli Normal file
View file

@ -0,0 +1,472 @@
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Basic String Utils} *)
type 'a gen = unit -> 'a option
type 'a sequence = ('a -> unit) -> unit
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
(** {2 Common Signature} *)
module type S = sig
type t
val length : t -> int
(** Return the length (number of characters) of the given string. *)
val blit : src:t -> src_pos:int -> dst:Bytes.t -> dst_pos:int -> len:int -> unit
(** Like {!String.blit}.
Compatible with the [-safe-string] option.
@raise Invalid_argument if indices are not valid. *)
(*
val blit_immut : t -> int -> t -> int -> int -> string
(** Immutable version of {!blit}, returning a new string.
[blit a i b j len] is the same as [b], but in which
the range [j, ..., j+len] is replaced by [a.[i], ..., a.[i + len]].
@raise Invalid_argument if indices are not valid. *)
*)
val fold : f:('a -> char -> 'a) -> init:'a -> t -> 'a
(** Fold on chars by increasing index.
@since 0.7 *)
(** {2 Conversions} *)
val to_gen : t -> char gen
(** Return the [gen] of characters contained in the string. *)
val to_seq : t -> char sequence
(** Return the [sequence] of characters contained in the string. *)
val to_klist : t -> char klist
(** Return the [klist] of characters contained in the string. *)
val to_list : t -> char list
(** Return the list of characters contained in the string. *)
val pp_buf : Buffer.t -> t -> unit
(** Renamed from [pp] since 2.0. *)
val pp : Format.formatter -> t -> unit
(** Print the string within quotes.
Renamed from [print] since 2.0. *)
end
(** {2 Strings} *)
include module type of struct include StringLabels end
val equal : string -> string -> bool
(** Equality function on strings. *)
val compare : string -> string -> int
val is_empty : string -> bool
(** [is_empty s] returns [true] iff [s] is empty (i.e. its length is 0).
@since 1.5 *)
val hash : string -> int
val init : int -> (int -> char) -> string
(** Like [Array.init].
@since 0.3.3 *)
val rev : string -> string
(** [rev s] returns the reverse of [s].
@since 0.17 *)
val pad : ?side:[`Left|`Right] -> ?c:char -> int -> string -> string
(** [pad n str] ensures that [str] is at least [n] bytes long,
and pads it on the [side] with [c] if it's not the case.
@param side determines where padding occurs (default: [`Left]).
@param c the char used to pad (default: ' ').
@since 0.17 *)
val of_char : char -> string
(** [of_char 'a'] is ["a"].
@since 0.19 *)
val of_gen : char gen -> string
(** Convert a [gen] of characters to a string. *)
val of_seq : char sequence -> string
(** Convert a [sequence] of characters to a string. *)
val of_klist : char klist -> string
(** Convert a [klist] of characters to a string. *)
val of_list : char list -> string
(** Convert a list of characters to a string. *)
val of_array : char array -> string
(** Convert an array of characters to a string. *)
val to_array : string -> char array
(** Return the array of characters contained in the string. *)
val find : ?start:int -> sub:(string [@keep_label]) -> string -> int
(** Find [sub] in string, returns its first index or [-1]. *)
val find_all : ?start:int -> sub:(string [@keep_label]) -> string -> int gen
(** [find_all ~sub s] finds all occurrences of [sub] in [s], even overlapping
instances.
@param start starting position in [s].
@since 0.17 *)
val find_all_l : ?start:int -> sub:(string [@keep_label]) -> string -> int list
(** [find_all_l ~sub s] finds all occurrences of [sub] in [s] and returns
them in a list.
@param start starting position in [s].
@since 0.17 *)
val mem : ?start:int -> sub:(string [@keep_label]) -> string -> bool
(** [mem ~sub s] is [true] iff [sub] is a substring of [s].
@since 0.12 *)
val rfind : sub:(string [@keep_label]) -> string -> int
(** Find [sub] in string from the right, returns its first index or [-1].
Should only be used with very small [sub].
@since 0.12 *)
val replace : ?which:[`Left|`Right|`All] -> sub:(string [@keep_label]) -> by:(string [@keep_label]) -> string -> string
(** [replace ~sub ~by s] replaces some occurrences of [sub] by [by] in [s].
@param which decides whether the occurrences to replace are:
{ul
{- [`Left] first occurrence from the left (beginning).}
{- [`Right] first occurrence from the right (end).}
{- [`All] all occurrences (default).}
}
@raise Invalid_argument if [sub = ""].
@since 0.14 *)
val is_sub : sub:(string [@keep_label]) -> sub_pos:int -> string -> pos:int -> sub_len:(int [@keep_label]) -> bool
(** [is_sub ~sub i s j ~len] returns [true] iff the substring of
[sub] starting at position [i] and of length [len] is a substring
of [s] starting at position [j]. *)
val repeat : string -> int -> string
(** The same string, repeated n times. *)
val prefix : pre:(string [@keep_label]) -> string -> bool
(** [prefix ~pre s] returns [true] iff [pre] is a prefix of [s]. *)
val suffix : suf:(string [@keep_label]) -> string -> bool
(** [suffix ~suf s] returns [true] iff [suf] is a suffix of [s].
@since 0.7 *)
val chop_prefix : pre:(string [@keep_label]) -> string -> string option
(** [chop_prefix ~pre s] removes [pre] from [s] if [pre] really is a prefix
of [s], returns [None] otherwise.
@since 0.17 *)
val chop_suffix : suf:(string [@keep_label]) -> string -> string option
(** [chop_suffix ~suf s] removes [suf] from [s] if [suf] really is a suffix
of [s], returns [None] otherwise.
@since 0.17 *)
val take : int -> string -> string
(** [take n s] keeps only the [n] first chars of [s].
@since 0.17 *)
val drop : int -> string -> string
(** [drop n s] removes the [n] first chars of [s].
@since 0.17 *)
val take_drop : int -> string -> string * string
(** [take_drop n s = take n s, drop n s].
@since 0.17 *)
val lines : string -> string list
(** [lines s] returns a list of the lines of [s] (splits along '\n').
@since 0.10 *)
val lines_gen : string -> string gen
(** [lines_gen s] returns a generator of the lines of [s] (splits along '\n').
@since 0.10 *)
val concat_gen : sep:(string [@keep_label]) -> string gen -> string
(** [concat_gen ~sep g] concatenates all strings of [g], separated with [sep].
@since 0.10 *)
val unlines : string list -> string
(** [unlines l] concatenates all strings of [l], separated with '\n'.
@since 0.10 *)
val unlines_gen : string gen -> string
(** [unlines_gen g] concatenates all strings of [g], separated with '\n'.
@since 0.10 *)
val set : string -> int -> char -> string
(** [set s i c] creates a new string which is a copy of [s], except
for index [i], which becomes [c].
@raise Invalid_argument if [i] is an invalid index.
@since 0.12 *)
val iter : f:(char -> unit) -> string -> unit
(** Alias to {!String.iter}.
@since 0.12 *)
val iteri : f:(int -> char -> unit) -> string -> unit
(** Iter on chars with their index.
@since 0.12 *)
val map : f:(char -> char) -> string -> string
(** Map chars.
@since 0.12 *)
val mapi : f:(int -> char -> char) -> string -> string
(** Map chars with their index.
@since 0.12 *)
val filter_map : f:(char -> char option) -> string -> string
(** [filter_map f s] calls [(f a0) (f a1) ... (f an)] where [a0 ... an] are the characters of s.
It returns the string of characters [ci] such as [f ai = Some ci] (when [f] returns [None],
the corresponding element of [s] is discarded).
@since 0.17 *)
val filter : f:(char -> bool) -> string -> string
(** [filter f s] discards characters not satisfying [f].
@since 0.17 *)
val flat_map : ?sep:string -> f:(char -> string) -> string -> string
(** Map each chars to a string, then concatenates them all.
@param sep optional separator between each generated string.
@since 0.12 *)
val for_all : f:(char -> bool) -> string -> bool
(** True for all chars?
@since 0.12 *)
val exists : f:(char -> bool) -> string -> bool
(** True for some char?
@since 0.12 *)
include S with type t := string
val drop_while : f:(char -> bool) -> t -> t
(** [drop_while f s] discards any characters starting from the left,
up to the first character [c] not satisfying [f c].
@since 2.2 *)
val rdrop_while : f:(char -> bool) -> t -> t
(** [rdrop_while f s] discards any characters starting from the right,
up to the first character [c] not satisfying [f c].
@since 2.2 *)
val ltrim : t -> t
(** Trim space on the left (see {!String.trim} for more details).
@since 1.2 *)
val rtrim : t -> t
(** Trim space on the right (see {!String.trim} for more details).
@since 1.2 *)
(** {2 Operations on 2 strings} *)
val map2 : f:(char -> char -> char) -> string -> string -> string
(** Map pairs of chars.
@raise Invalid_argument if the strings have not the same length.
@since 0.12 *)
val iter2: f:(char -> char -> unit) -> string -> string -> unit
(** Iterate on pairs of chars.
@raise Invalid_argument if the strings have not the same length.
@since 0.12 *)
val iteri2: f:(int -> char -> char -> unit) -> string -> string -> unit
(** Iterate on pairs of chars with their index.
@raise Invalid_argument if the strings have not the same length.
@since 0.12 *)
val fold2: f:('a -> char -> char -> 'a) -> init:'a -> string -> string -> 'a
(** Fold on pairs of chars.
@raise Invalid_argument if the strings have not the same length.
@since 0.12 *)
val for_all2 : f:(char -> char -> bool) -> string -> string -> bool
(** All pairs of chars respect the predicate?
@raise Invalid_argument if the strings have not the same length.
@since 0.12 *)
val exists2 : f:(char -> char -> bool) -> string -> string -> bool
(** Exists a pair of chars?
@raise Invalid_argument if the strings have not the same length.
@since 0.12 *)
(** {2 Ascii functions}
Those functions are deprecated in {!String} since 4.03, so we provide
a stable alias for them even in older versions. *)
val capitalize_ascii : string -> string
(** See {!String}.
@since 0.18 *)
val uncapitalize_ascii : string -> string
(** See {!String}.
@since 0.18 *)
val uppercase_ascii : string -> string
(** See {!String}.
@since 0.18 *)
val lowercase_ascii : string -> string
(** See {!String}.
@since 0.18 *)
val equal_caseless : string -> string -> bool
(** Comparison without respect to {b ascii} lowercase.
@since 1.2 *)
(** {2 Finding}
A relatively efficient algorithm for finding sub-strings.
@since 1.0 *)
module Find : sig
type _ pattern
val compile : string -> [ `Direct ] pattern
val rcompile : string -> [ `Reverse ] pattern
val find : ?start:int -> pattern:(([`Direct] pattern) [@keep_label]) -> string -> int
(** Search for [pattern] in the string, left-to-right.
@return the offset of the first match, -1 otherwise.
@param start offset in string at which we start. *)
val rfind : ?start:int -> pattern:(([`Reverse] pattern) [@keep_label]) -> string -> int
(** Search for [pattern] in the string, right-to-left.
@return the offset of the start of the first match from the right, -1 otherwise.
@param start right-offset in string at which we start. *)
end
(** {2 Splitting} *)
module Split : sig
(** Specification of what to do with empty blocks, as in [split ~by:"-" "-a-b-"].
- [{first=false; last=false}] will return [""; "a"; "b"; ""]
- [{first=true; last=false}] will return ["a"; "b" ""]
- [{first=false; last=true}] will return [""; "a"; "b"]
- [{first=true; last=true}] will return ["a"; "b"]
The default value of all remaining functions is [Drop_none].
@since 1.5
*)
type drop_if_empty = {
first: bool;
last: bool;
}
val no_drop : drop_if_empty
(** Do not drop any group, even empty and on borders.
@since 1.5 *)
val list_ : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> (string*int*int) list
(** Split the given string along the given separator [by]. Should only
be used with very small separators, otherwise
use {!Containers_string.KMP}.
@return a list of slices [(s,index,length)] that are
separated by [by]. {!String.sub} can then be used to actually extract
a string from the slice.
@raise Failure if [by = ""]. *)
val gen : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> (string*int*int) gen
val seq : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> (string*int*int) sequence
val klist : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> (string*int*int) klist
(** {4 Copying functions}
Those split functions actually copy the substrings, which can be
more convenient but less efficient in general. *)
val list_cpy : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> string list
val gen_cpy : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> string gen
val seq_cpy : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> string sequence
val klist_cpy : ?drop:drop_if_empty -> by:(string [@keep_label]) -> string -> string klist
val left : by:(string [@keep_label]) -> string -> (string * string) option
(** Split on the first occurrence of [by] from the leftmost part of
the string.
@since 0.12 *)
val left_exn : by:(string [@keep_label]) -> string -> string * string
(** Split on the first occurrence of [by] from the leftmost part of the string.
@raise Not_found if [by] is not part of the string.
@since 0.16 *)
val right : by:(string [@keep_label]) -> string -> (string * string) option
(** Split on the first occurrence of [by] from the rightmost part of
the string.
@since 0.12 *)
val right_exn : by:(string [@keep_label]) -> string -> string * string
(** Split on the first occurrence of [by] from the rightmost part of the string.
@raise Not_found if [by] is not part of the string.
@since 0.16 *)
end
val split_on_char : by:char -> string -> string list
(** Split the string along the given char.
@since 1.2 *)
val split : by:(string [@keep_label]) -> string -> string list
(** Alias to {!Split.list_cpy}.
@since 1.2 *)
(** {2 Utils} *)
val compare_versions : string -> string -> int
(** [compare_versions a b] compares {i version strings} [ a] and [b],
considering that numbers are above text.
@since 0.13 *)
val compare_natural : string -> string -> int
(** Natural Sort Order, comparing chunks of digits as natural numbers.
https://en.wikipedia.org/wiki/Natural_sort_order
@since 1.3 *)
val edit_distance : string -> string -> int
(** Edition distance between two strings. This satisfies the classical
distance axioms: it is always positive, symmetric, and satisfies
the formula [distance a b + distance b c >= distance a c]. *)
(** {2 Slices}
A contiguous part of a string *)
module Sub : sig
type t = string * int * int
(** A string, an offset, and the length of the slice. *)
val make : string -> pos:int -> len:(int [@keep_label]) -> t
val full : string -> t
(** Full string. *)
val copy : t -> string
(** Make a copy of the substring. *)
val underlying : t -> string
val sub : t -> int -> int -> t
(** Sub-slice. *)
val get : t -> int -> char
(** [get s i] gets the [i]-th element, or fails.
@raise Invalid_argument if the index is not within [0 ... length - 1].
@since 1.2 *)
include S with type t := t
end

View file

@ -6,9 +6,11 @@
module Array = CCArray
module ArrayLabels = CCArrayLabels
module Array_slice = CCArray_slice
module Array_sliceLabels = CCArray_sliceLabels
module Bool = CCBool
module Char = Char
module Equal = CCEqual
module EqualLabels = CCEqualLabels
module Float = CCFloat
module Format = CCFormat
module Fun = CCFun
@ -43,6 +45,7 @@ module Ref = CCRef
module Result = CCResult
module Set = CCSet
module String = CCString
module StringLabels = CCStringLabels
module Vector = CCVector
module Monomorphic = CCMonomorphic
module Utf8_string = CCUtf8_string

View file

@ -1,3 +1,32 @@
(rule
(targets CCArray.mli)
(deps CCArrayLabels.mli)
(mode promote)
(action (run ../unlabel.exe %{deps} %{targets})))
(rule
(targets CCArray_slice.mli)
(deps CCArray_sliceLabels.mli)
(mode promote)
(action (run ../unlabel.exe %{deps} %{targets})))
(rule
(targets CCEqual.mli)
(deps CCEqualLabels.mli)
(mode promote)
(action (run ../unlabel.exe %{deps} %{targets})))
(rule
(targets CCList.mli)
(deps CCListLabels.mli)
(mode promote)
(action (run ../unlabel.exe %{deps} %{targets})))
(rule
(targets CCString.mli)
(deps CCStringLabels.mli)
(mode promote)
(action (run ../unlabel.exe %{deps} %{targets})))
(library
(name containers)

View file

@ -1,12 +1,16 @@
(executable
(name mkflags)
(modules mkflags)
(libraries dune.configurator))
(executable
(name unlabel)
(modules unlabel)
(libraries str compiler-libs.common))
(rule
(targets flambda.flags)
(mode fallback)
(action
(run ./mkflags.exe))
)

246
src/unlabel.ml Normal file
View file

@ -0,0 +1,246 @@
(* search for first occurence of pat in s *)
let rec search pat s pos =
let rec compare i =
if i >= String.length pat
then true
else if pat.[i] = s.[pos+i]
then compare (i+1)
else false
in
if pos > String.length s - String.length pat
then raise Not_found
else if compare 0
then pos
else search pat s (pos+1)
;;
(* search all non-overlapping occurences of pat in s *)
let search_all pat s =
let rec search_rest acc pos =
let next =
try Some (search pat s pos) with
Not_found -> None
in
match next with
| None -> acc
| Some pos -> search_rest (pos::acc) (pos + String.length pat)
in
List.rev (search_rest [] 0)
;;
(* replase first occurence of pat with subst in s *)
let replace_first pat subst s =
let pos = search pat s 0 in
let patl = String.length pat
and substl = String.length subst in
let buf = Bytes.create (String.length s - patl + substl) in
Bytes.blit_string s 0 buf 0 pos;
Bytes.blit_string subst 0 buf pos substl;
Bytes.blit_string
s (pos + patl)
buf (pos + substl)
(String.length s - pos - patl);
Bytes.unsafe_to_string buf
;;
(* replase first occurence of pat with subst in s *)
let replace_all pat subst s =
let pos = search_all pat s in
let patl = String.length pat
and substl = String.length subst in
let len = String.length s + List.length pos * (substl - patl) in
let buf = Bytes.create len in
let rec loop src_pos dst_pos = function
| [] ->
Bytes.blit_string s src_pos buf dst_pos (String.length s - src_pos)
| pat_pos :: tail ->
let headl = pat_pos - src_pos in
Bytes.blit_string s src_pos buf dst_pos headl;
Bytes.blit_string subst 0 buf (dst_pos + headl) substl;
loop
(src_pos + headl + patl)
(dst_pos + headl + substl)
tail
in loop 0 0 pos;
Bytes.unsafe_to_string buf
;;
let match_closeparen s i =
assert (s.[i] = ')');
let rec loop i count =
match s.[i] with
| '(' when count = 0 -> i
| '(' -> loop (i-1) (count-1)
| ')' -> loop (i-1) (count+1)
| _ -> loop (i-1) count
in loop (i-1) 0
;;
let slurp_file file =
let ch = open_in file in
let buf = Buffer.create (min 1024 (in_channel_length ch)) in
try
while true do Buffer.add_channel buf ch 4096 done;
assert false
with
| End_of_file ->
close_in ch;
Bytes.unsafe_to_string (Buffer.to_bytes buf)
;;
let () =
assert (Array.length Sys.argv = 3);
let labelled_filename = Sys.argv.(1) in (* CCArrayLabels.mli *)
let unlabelled_filename = Sys.argv.(2) in (* CCArray.ml *)
let labelled_name = (* ArrayLabels *)
let basename =
Compenv.module_of_filename Format.err_formatter
labelled_filename
labelled_filename
in
assert (basename.[0] = 'C' && basename.[1] = 'C');
String.sub basename 2 (String.length basename - 2)
in
let unlabelled_name = (* Array *)
replace_first "Labels" "" labelled_name
in
let labelled_text = slurp_file labelled_filename in
let lexbuf = Lexing.from_string labelled_text in
Location.init lexbuf labelled_filename;
let labelled_ast = Parse.interface lexbuf in
(* stack of replacements to perform on the labelled_text.
* perform them in one run later so that the character counts
* won't be affected by earlier replacements. *)
let replacements = ref [] in
(* function removing '~' from docstring attributes where appropriate. *)
let strip_attributes labels attributes =
List.iter
begin function
| ({ Asttypes.txt = "ocaml.doc"; _ },
Parsetree.PStr [{pstr_loc =
{ loc_start = {pos_cnum = start; _}
; loc_end = {pos_cnum = stop; _}
; _}
; _
}]) ->
let docstring =
List.fold_left
(fun docstring label ->
replace_all ("~" ^ label) label docstring)
(String.sub labelled_text start (stop-start))
labels
in
replacements := (start, stop-start, docstring) :: !replacements
| _ -> ()
end
attributes
in
let iterator =
let open Ast_iterator in
let open Parsetree in
{ Ast_iterator.default_iterator with
value_description = begin fun iterator
{ pval_name = { txt = _name; _ }
; pval_type
; pval_prim = _
; pval_attributes
; pval_loc
} ->
let rec loop = function
(* match function type with label *)
| { ptyp_desc = Ptyp_arrow (Labelled label, left, right)
; ptyp_loc = {loc_start = {Lexing.pos_cnum = start; _}; _}
; ptyp_attributes
; _}
when
(* check that the argument type is not marked with [@keep_label] *)
List.for_all
(fun ({Asttypes.txt; _}, _) -> txt <> "keep_label")
left.ptyp_attributes
->
assert (label = String.sub labelled_text start (String.length label));
let colon = String.index_from labelled_text start ':' in
(* remove label *)
replacements := (start, colon+1-start, "") :: !replacements;
(* remove labels from associated docstrings *)
strip_attributes [label] ptyp_attributes;
label :: loop right
| { ptyp_desc = Ptyp_arrow (_, _left, right); _} ->
loop right
| _ -> []
in
let labels = loop pval_type in
strip_attributes labels pval_attributes;
iterator.attributes iterator pval_attributes;
iterator.location iterator pval_loc;
iterator.typ iterator pval_type;
end
; attribute = begin fun iterator
({Asttypes.txt
; loc =
{ loc_start = {pos_cnum = start; _}
; loc_end = {pos_cnum = stop; _}
; _} as loc
}, _) ->
if txt = "keep_label"
then begin
(* start and stop positions mark the location of only the label name.
* Therefore search for enclosing brackets. *)
let start = String.rindex_from labelled_text start '['
and stop = String. index_from labelled_text stop ']' in
(* remove leading ' ', too *)
let start =
if labelled_text.[start-1] = ' ' then start-1 else start
in
(* if a closing paren follows, remove this and the matching paren,
* this will hopefully be the right thing to do. *)
let stop =
if labelled_text.[stop+1] = ')'
then
let openp = match_closeparen labelled_text (stop+1) in
replacements := (openp, 1, "") :: !replacements;
stop+1
else
stop
in
replacements := (start, stop-start+1, "") :: !replacements;
end;
iterator.location iterator loc
end
}
in
iterator.signature iterator labelled_ast;
(* sort replacements in ascending order. *)
let replacements =
List.sort (fun (p1,_,_) (p2,_,_) -> compare p1 p2) !replacements
in
(* perform the replacements by blitting to a buffer. *)
let unlabelled_text = Buffer.create (String.length labelled_text) in
List.fold_left begin fun start (pos,len,subst) ->
assert (pos >= start);
Buffer.add_substring unlabelled_text labelled_text start (pos - start);
Buffer.add_string unlabelled_text subst;
pos+len
end
0
replacements
|> fun start ->
Buffer.add_substring unlabelled_text
labelled_text start (String.length labelled_text - start);
let unlabelled_text =
Buffer.contents unlabelled_text
(* CCArrayLabels -> CCArray *)
|> replace_all labelled_name unlabelled_name
in
let out = open_out unlabelled_filename in
output_string out (
"(* AUTOGENERATED FROM " ^
labelled_filename
^ " *)\n\n");
output_string out unlabelled_text;
close_out out;
;;