mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-07 11:45:31 -05:00
Merge branch 'master' into add_negate
This commit is contained in:
commit
cbff4e5a39
7 changed files with 165 additions and 18 deletions
|
|
@ -19,4 +19,5 @@
|
||||||
- Malcolm Matalka (`orbitz`)
|
- Malcolm Matalka (`orbitz`)
|
||||||
- David Sheets (@dsheets)
|
- David Sheets (@dsheets)
|
||||||
- Glenn Slotte (glennsl)
|
- Glenn Slotte (glennsl)
|
||||||
|
- @LemonBoy
|
||||||
- Leonid Rozenberg (@rleonid)
|
- Leonid Rozenberg (@rleonid)
|
||||||
|
|
@ -39,6 +39,46 @@ let nativeint fmt n = Format.fprintf fmt "%nd" n
|
||||||
let string_quoted fmt s = Format.fprintf fmt "\"%s\"" s
|
let string_quoted fmt s = Format.fprintf fmt "\"%s\"" s
|
||||||
let flush = Format.pp_print_flush
|
let flush = Format.pp_print_flush
|
||||||
|
|
||||||
|
let newline = Format.pp_force_newline
|
||||||
|
|
||||||
|
let substring out (s,i,len): unit =
|
||||||
|
string out (String.sub s i len)
|
||||||
|
|
||||||
|
let text out (s:string): unit =
|
||||||
|
let len = String.length s in
|
||||||
|
let i = ref 0 in
|
||||||
|
let search_ c =
|
||||||
|
try Some (String.index_from s !i c) with Not_found -> None
|
||||||
|
in
|
||||||
|
while !i < len do
|
||||||
|
let j_newline = search_ '\n' in
|
||||||
|
let j_space = search_ ' ' in
|
||||||
|
let on_newline j =
|
||||||
|
substring out (s, !i, j - !i);
|
||||||
|
newline out ();
|
||||||
|
i := j + 1
|
||||||
|
and on_space j =
|
||||||
|
substring out (s, !i, j - !i);
|
||||||
|
Format.pp_print_space out ();
|
||||||
|
i := j + 1
|
||||||
|
in
|
||||||
|
begin match j_newline, j_space with
|
||||||
|
| None, None ->
|
||||||
|
(* done *)
|
||||||
|
substring out (s, !i, len - !i);
|
||||||
|
i := len
|
||||||
|
| Some j, None -> on_newline j
|
||||||
|
| None, Some j -> on_space j
|
||||||
|
| Some j1, Some j2 ->
|
||||||
|
if j1<j2 then on_newline j1 else on_space j2
|
||||||
|
end
|
||||||
|
done
|
||||||
|
|
||||||
|
(*$= & ~printer:(fun s->CCFormat.sprintf "%S" s)
|
||||||
|
"a\nb\nc" (sprintf_no_color "@[<v>%a@]%!" text "a b c")
|
||||||
|
"a b\nc" (sprintf_no_color "@[<h>%a@]%!" text "a b\nc")
|
||||||
|
*)
|
||||||
|
|
||||||
let list ?(sep=return ",@ ") pp fmt l =
|
let list ?(sep=return ",@ ") pp fmt l =
|
||||||
let rec pp_list l = match l with
|
let rec pp_list l = match l with
|
||||||
| x::((_::_) as l) ->
|
| x::((_::_) as l) ->
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,23 @@ val bool : bool printer
|
||||||
val float3 : float printer (* 3 digits after . *)
|
val float3 : float printer (* 3 digits after . *)
|
||||||
val float : float printer
|
val float : float printer
|
||||||
|
|
||||||
|
val newline : unit printer
|
||||||
|
(** Force newline (see {!Format.pp_force_newline})
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
|
val substring : (string * int * int) printer
|
||||||
|
(** Print the substring [(s,i,len)], where [i] is the offset
|
||||||
|
in [s] and [len] the number of bytes in the substring.
|
||||||
|
@raise Invalid_argument if the triple [(s,i,len)] does not
|
||||||
|
describe a proper substring.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
|
val text : string printer
|
||||||
|
(** Print string, but replacing spaces with breaks and newlines
|
||||||
|
with {!newline}.
|
||||||
|
See [pp_print_text] on recent versions of OCaml.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
val char : char printer (** @since 0.14 *)
|
val char : char printer (** @since 0.14 *)
|
||||||
val int32 : int32 printer (** @since 0.14 *)
|
val int32 : int32 printer (** @since 0.14 *)
|
||||||
val int64 : int64 printer (** @since 0.14 *)
|
val int64 : int64 printer (** @since 0.14 *)
|
||||||
|
|
|
||||||
|
|
@ -339,6 +339,52 @@ let partition_map f l =
|
||||||
assert_equal [1;3] l2
|
assert_equal [1;3] l2
|
||||||
*)
|
*)
|
||||||
|
|
||||||
|
let combine l1 l2 =
|
||||||
|
let rec direct i l1 l2 = match l1, l2 with
|
||||||
|
| ([], []) -> []
|
||||||
|
| _ when i=0 -> safe l1 l2 []
|
||||||
|
| (x1::l1', x2::l2') -> (x1, x2) :: direct (i-1) l1' l2'
|
||||||
|
| (_, _) -> invalid_arg "CCList.combine"
|
||||||
|
and safe l1 l2 acc = match l1, l2 with
|
||||||
|
| ([], []) -> List.rev acc
|
||||||
|
| (x1::l1', x2::l2') -> safe l1' l2' @@ (x1, x2) :: acc
|
||||||
|
| (_, _) -> invalid_arg "CCList.combine"
|
||||||
|
in
|
||||||
|
direct direct_depth_default_ l1 l2
|
||||||
|
|
||||||
|
(*$T
|
||||||
|
try ignore (combine [1] []); false with Invalid_argument _ -> true
|
||||||
|
try ignore (combine (1--1001) (1--1002)); false with Invalid_argument _ -> true
|
||||||
|
combine [1;2;3] [3;2;1] = List.combine [1;2;3] [3;2;1]
|
||||||
|
combine (1 -- 100_000) (1 -- 100_000) = List.combine (1 -- 100_000) (1 -- 100_000)
|
||||||
|
*)
|
||||||
|
|
||||||
|
(*$Q
|
||||||
|
Q.(let p = small_list int in pair p p)(fun (l1,l2) -> \
|
||||||
|
if List.length l1=List.length l2 \
|
||||||
|
then CCList.combine l1 l2 = List.combine l1 l2 \
|
||||||
|
else Q.assume_fail() )
|
||||||
|
*)
|
||||||
|
|
||||||
|
let combine_gen l1 l2 =
|
||||||
|
let l1 = ref l1 in
|
||||||
|
let l2 = ref l2 in
|
||||||
|
fun () -> match !l1, !l2 with
|
||||||
|
| [], _
|
||||||
|
| _, [] -> None
|
||||||
|
| x1 :: tail1, x2 :: tail2 ->
|
||||||
|
l1 := tail1;
|
||||||
|
l2 := tail2;
|
||||||
|
Some (x1,x2)
|
||||||
|
|
||||||
|
(*$Q
|
||||||
|
Q.(let p = small_list int in pair p p)(fun (l1,l2) -> \
|
||||||
|
let n = min (List.length l1) (List.length l2) in \
|
||||||
|
let res1 = combine (take n l1) (take n l2) in \
|
||||||
|
let res2 = combine_gen l1 l2 |> of_gen in \
|
||||||
|
res1 = res2)
|
||||||
|
*)
|
||||||
|
|
||||||
let return x = [x]
|
let return x = [x]
|
||||||
|
|
||||||
let (>>=) l f = flat_map f l
|
let (>>=) l f = flat_map f l
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
|
|
||||||
(** {1 complements to list} *)
|
(** {1 complements to list} *)
|
||||||
|
|
||||||
|
type 'a sequence = ('a -> unit) -> unit
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
|
type 'a printer = Format.formatter -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
type 'a t = 'a list
|
type 'a t = 'a list
|
||||||
|
|
||||||
val empty : 'a t
|
val empty : 'a t
|
||||||
|
|
@ -72,6 +78,18 @@ val init : int -> (int -> 'a) -> 'a t
|
||||||
(** Similar to {!Array.init}
|
(** Similar to {!Array.init}
|
||||||
@since 0.6 *)
|
@since 0.6 *)
|
||||||
|
|
||||||
|
val combine : 'a list -> 'b list -> ('a * 'b) list
|
||||||
|
(** Similar to {!List.combine} but tail-recursive.
|
||||||
|
@raise Invalid_argument if the lists have distinct lengths.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
|
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 NEXT_RELEASE *)
|
||||||
|
|
||||||
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
|
val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
|
||||||
|
|
||||||
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
|
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
|
||||||
|
|
@ -433,12 +451,6 @@ end
|
||||||
|
|
||||||
(** {2 Conversions} *)
|
(** {2 Conversions} *)
|
||||||
|
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
|
||||||
type 'a gen = unit -> 'a option
|
|
||||||
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
|
||||||
type 'a printer = Format.formatter -> 'a -> unit
|
|
||||||
type 'a random_gen = Random.State.t -> 'a
|
|
||||||
|
|
||||||
val random : 'a random_gen -> 'a t random_gen
|
val random : 'a random_gen -> 'a t random_gen
|
||||||
val random_non_empty : 'a random_gen -> 'a t random_gen
|
val random_non_empty : 'a random_gen -> 'a t random_gen
|
||||||
val random_len : int -> 'a random_gen -> 'a t random_gen
|
val random_len : int -> 'a random_gen -> 'a t random_gen
|
||||||
|
|
|
||||||
|
|
@ -391,16 +391,7 @@ end
|
||||||
let split_on_char c s: _ list =
|
let split_on_char c s: _ list =
|
||||||
Split.list_cpy ~by:(String.make 1 c) s
|
Split.list_cpy ~by:(String.make 1 c) s
|
||||||
|
|
||||||
(*$= & ~printer:Q.Print.(list string)
|
let split = Split.list_cpy
|
||||||
["a"; "few"; "words"; "from"; "our"; "sponsors"] \
|
|
||||||
(split_on_char ' ' "a few words from our sponsors")
|
|
||||||
*)
|
|
||||||
|
|
||||||
(*$Q
|
|
||||||
Q.(printable_string) (fun s -> \
|
|
||||||
let s = split_on_char ' ' s |> String.concat " " in \
|
|
||||||
s = split_on_char ' ' s |> String.concat " ")
|
|
||||||
*)
|
|
||||||
|
|
||||||
let compare_versions a b =
|
let compare_versions a b =
|
||||||
let of_int s = try Some (int_of_string s) with _ -> None in
|
let of_int s = try Some (int_of_string s) with _ -> None in
|
||||||
|
|
@ -719,7 +710,16 @@ let lowercase_ascii = map CCChar.lowercase_ascii
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
let equal_caseless s1 s2: bool =
|
||||||
|
let char_lower c =
|
||||||
|
if c >= 'A' && c <= 'Z'
|
||||||
|
then Char.unsafe_chr (Char. code c + 32)
|
||||||
|
else c
|
||||||
|
in
|
||||||
|
String.length s1 = String.length s2 &&
|
||||||
|
for_all2
|
||||||
|
(fun c1 c2 -> char_lower c1 = char_lower c2)
|
||||||
|
s1 s2
|
||||||
|
|
||||||
let pp buf s =
|
let pp buf s =
|
||||||
Buffer.add_char buf '"';
|
Buffer.add_char buf '"';
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,22 @@ val uppercase_ascii : string -> string
|
||||||
val lowercase_ascii : string -> string
|
val lowercase_ascii : string -> string
|
||||||
(** See {!String}. @since 0.18 *)
|
(** See {!String}. @since 0.18 *)
|
||||||
|
|
||||||
|
val equal_caseless : string -> string -> bool
|
||||||
|
(** Comparison without respect to {b ascii} lowercase.
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
|
(*$T
|
||||||
|
equal_caseless "foo" "FoO"
|
||||||
|
equal_caseless "helLo" "HEllO"
|
||||||
|
*)
|
||||||
|
|
||||||
|
(*$Q
|
||||||
|
Q.(pair printable_string printable_string) (fun (s1,s2) -> \
|
||||||
|
equal_caseless s1 s2 = (lowercase_ascii s1=lowercase_ascii s2))
|
||||||
|
Q.(printable_string) (fun s -> equal_caseless s s)
|
||||||
|
Q.(printable_string) (fun s -> equal_caseless (uppercase_ascii s) s)
|
||||||
|
*)
|
||||||
|
|
||||||
(** {2 Finding}
|
(** {2 Finding}
|
||||||
|
|
||||||
A relatively efficient algorithm for finding sub-strings
|
A relatively efficient algorithm for finding sub-strings
|
||||||
|
|
@ -499,6 +515,21 @@ val split_on_char : char -> string -> string list
|
||||||
(** Split the string along the given char
|
(** Split the string along the given char
|
||||||
@since NEXT_RELEASE *)
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
|
(*$= & ~printer:Q.Print.(list string)
|
||||||
|
["a"; "few"; "words"; "from"; "our"; "sponsors"] \
|
||||||
|
(split_on_char ' ' "a few words from our sponsors")
|
||||||
|
*)
|
||||||
|
|
||||||
|
(*$Q
|
||||||
|
Q.(printable_string) (fun s -> \
|
||||||
|
let s = split_on_char ' ' s |> String.concat " " in \
|
||||||
|
s = (split_on_char ' ' s |> String.concat " "))
|
||||||
|
*)
|
||||||
|
|
||||||
|
val split : by:string -> string -> string list
|
||||||
|
(** Alias to {!Split.list_cpy}
|
||||||
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
(** {2 Utils} *)
|
(** {2 Utils} *)
|
||||||
|
|
||||||
val compare_versions : string -> string -> int
|
val compare_versions : string -> string -> int
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue