mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
1607 lines
34 KiB
OCaml
1607 lines
34 KiB
OCaml
(* backport new functions from stdlib here *)
|
|
|
|
[@@@ocaml.warning "-32"]
|
|
|
|
let rec compare_lengths l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> 0
|
|
| [], _ :: _ -> -1
|
|
| _ :: _, [] -> 1
|
|
| _ :: tail1, _ :: tail2 -> compare_lengths tail1 tail2
|
|
|
|
let rec compare_length_with l n =
|
|
match l, n with
|
|
| _ when n < 0 -> 1
|
|
| [], 0 -> 0
|
|
| [], _ -> -1
|
|
| _ :: tail, _ -> compare_length_with tail (n - 1)
|
|
|
|
let rec assoc_opt x = function
|
|
| [] -> None
|
|
| (y, v) :: _ when Stdlib.( = ) x y -> Some v
|
|
| _ :: tail -> assoc_opt x tail
|
|
|
|
let rec assq_opt x = function
|
|
| [] -> None
|
|
| (y, v) :: _ when Stdlib.( == ) x y -> Some v
|
|
| _ :: tail -> assq_opt x tail
|
|
|
|
[@@@ocaml.warning "+32"]
|
|
|
|
(* end of backport *)
|
|
|
|
include List
|
|
|
|
let empty = []
|
|
|
|
let is_empty = function
|
|
| [] -> true
|
|
| _ :: _ -> false
|
|
|
|
let mguard c =
|
|
if c then
|
|
[ () ]
|
|
else
|
|
[]
|
|
|
|
(** max depth for direct recursion *)
|
|
let direct_depth_default_ = 1000
|
|
|
|
(* TRMC on >= 5.1, no need to bring our own *)
|
|
[@@@iflt 5.1]
|
|
|
|
let tail_map f l =
|
|
(* Unwind the list of tuples, reconstructing the full list front-to-back.
|
|
@param tail_acc a suffix of the final list; we append tuples' content
|
|
at the front of it *)
|
|
let rec rebuild tail_acc = function
|
|
| [] -> tail_acc
|
|
| (y0, y1, y2, y3, y4, y5, y6, y7, y8) :: bs ->
|
|
rebuild
|
|
(y0 :: y1 :: y2 :: y3 :: y4 :: y5 :: y6 :: y7 :: y8 :: tail_acc)
|
|
bs
|
|
in
|
|
(* Create a compressed reverse-list representation using tuples
|
|
@param tuple_acc a reverse list of chunks mapped with [f] *)
|
|
let rec dive tuple_acc = function
|
|
| x0 :: x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: xs ->
|
|
let y0 = f x0 in
|
|
let y1 = f x1 in
|
|
let y2 = f x2 in
|
|
let y3 = f x3 in
|
|
let y4 = f x4 in
|
|
let y5 = f x5 in
|
|
let y6 = f x6 in
|
|
let y7 = f x7 in
|
|
let y8 = f x8 in
|
|
dive ((y0, y1, y2, y3, y4, y5, y6, y7, y8) :: tuple_acc) xs
|
|
| xs ->
|
|
(* Reverse direction, finishing off with a direct map *)
|
|
let tail = List.map f xs in
|
|
rebuild tail tuple_acc
|
|
in
|
|
dive [] l
|
|
|
|
let map f l =
|
|
let rec direct f i l =
|
|
match l with
|
|
| [] -> []
|
|
| [ x ] -> [ f x ]
|
|
| [ x1; x2 ] ->
|
|
let y1 = f x1 in
|
|
[ y1; f x2 ]
|
|
| [ x1; x2; x3 ] ->
|
|
let y1 = f x1 in
|
|
let y2 = f x2 in
|
|
[ y1; y2; f x3 ]
|
|
| _ when i = 0 -> tail_map f l
|
|
| x1 :: x2 :: x3 :: x4 :: l' ->
|
|
let y1 = f x1 in
|
|
let y2 = f x2 in
|
|
let y3 = f x3 in
|
|
let y4 = f x4 in
|
|
y1 :: y2 :: y3 :: y4 :: direct f (i - 1) l'
|
|
in
|
|
direct f direct_depth_default_ l
|
|
|
|
let append l1 l2 =
|
|
let[@inline] safe l1 l2 = List.rev_append (List.rev l1) l2 in
|
|
let rec direct i l1 l2 =
|
|
match l1 with
|
|
| [] -> l2
|
|
| [ x ] -> x :: l2
|
|
| _ when i = 0 -> safe l1 l2
|
|
| x :: y :: tl1 -> x :: y :: direct (i - 1) tl1 l2
|
|
in
|
|
direct 1000 l1 l2
|
|
|
|
[@@@endif]
|
|
|
|
(* Wrapper around [append] to optimize for the case of short [l1],
|
|
and for the case of [l2 = []] (saves the whole copy of [l1]!) *)
|
|
let[@inline] append l1 l2 =
|
|
match l1, l2 with
|
|
| [], _ -> l2
|
|
| _, [] -> l1
|
|
| [ x ], _ -> x :: l2
|
|
| x :: y :: tl1, _ -> x :: y :: append tl1 l2
|
|
|
|
let ( @ ) = append
|
|
let[@inline] cons' l x = x :: l
|
|
|
|
let cons_maybe o l =
|
|
match o with
|
|
| Some x -> x :: l
|
|
| None -> l
|
|
|
|
(* TRMC after 5.1 *)
|
|
[@@@iflt 5.1]
|
|
|
|
let direct_depth_filter_ = 10_000
|
|
|
|
let filter p l =
|
|
let rec direct i p l =
|
|
match l with
|
|
| [] -> []
|
|
| _ when i = 0 -> safe p l []
|
|
| x :: l' when not (p x) -> direct i p l'
|
|
| x :: l' -> x :: direct (i - 1) p l'
|
|
and safe p l acc =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| x :: l' when not (p x) -> safe p l' acc
|
|
| x :: l' -> safe p l' (x :: acc)
|
|
in
|
|
direct direct_depth_filter_ p l
|
|
|
|
let fold_right f l acc =
|
|
let rec direct i f l acc =
|
|
match l with
|
|
| [] -> acc
|
|
| _ when i = 0 -> safe f (List.rev l) acc
|
|
| x :: l' ->
|
|
let acc = direct (i - 1) f l' acc in
|
|
f x acc
|
|
and safe f l acc =
|
|
match l with
|
|
| [] -> acc
|
|
| x :: l' ->
|
|
let acc = f x acc in
|
|
safe f l' acc
|
|
in
|
|
direct direct_depth_default_ f l acc
|
|
|
|
[@@@endif]
|
|
|
|
let rec fold_while f acc = function
|
|
| [] -> acc
|
|
| e :: l ->
|
|
let acc, cont = f acc e in
|
|
(match cont with
|
|
| `Stop -> acc
|
|
| `Continue -> fold_while f acc l)
|
|
|
|
let fold_map f acc l =
|
|
let rec aux f acc map_acc l =
|
|
match l with
|
|
| [] -> acc, List.rev map_acc
|
|
| x :: l' ->
|
|
let acc, y = f acc x in
|
|
aux f acc (y :: map_acc) l'
|
|
in
|
|
aux f acc [] l
|
|
|
|
let fold_map_i f acc l =
|
|
let rec aux f acc i map_acc l =
|
|
match l with
|
|
| [] -> acc, List.rev map_acc
|
|
| x :: l' ->
|
|
let acc, y = f acc i x in
|
|
aux f acc (i + 1) (y :: map_acc) l'
|
|
in
|
|
aux f acc 0 [] l
|
|
|
|
let fold_on_map ~f ~reduce acc l =
|
|
let rec aux acc l =
|
|
match l with
|
|
| [] -> acc
|
|
| x :: l' ->
|
|
let acc = reduce acc (f x) in
|
|
aux acc l'
|
|
in
|
|
aux acc l
|
|
|
|
let scan_left f acc l =
|
|
let rec aux f acc l_acc l =
|
|
match l with
|
|
| [] -> List.rev l_acc
|
|
| x :: tail ->
|
|
let acc = f acc x in
|
|
let l_acc = acc :: l_acc in
|
|
aux f acc l_acc tail
|
|
in
|
|
aux f acc [ acc ] l
|
|
|
|
let reduce f = function
|
|
| [] -> None
|
|
| x :: l -> Some (fold_left f x l)
|
|
|
|
let reduce_exn f = function
|
|
| [] -> raise (Invalid_argument "CCList.reduce_exn")
|
|
| x :: l -> fold_left f x l
|
|
|
|
let fold_map2 f acc l1 l2 =
|
|
let rec aux f acc map_acc l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> acc, List.rev map_acc
|
|
| [], _ | _, [] -> invalid_arg "fold_map2"
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let acc, y = f acc x1 x2 in
|
|
aux f acc (y :: map_acc) l1' l2'
|
|
in
|
|
aux f acc [] l1 l2
|
|
|
|
let fold_filter_map f acc l =
|
|
let rec aux f acc map_acc l =
|
|
match l with
|
|
| [] -> acc, List.rev map_acc
|
|
| x :: l' ->
|
|
let acc, y = f acc x in
|
|
aux f acc (cons_maybe y map_acc) l'
|
|
in
|
|
aux f acc [] l
|
|
|
|
let fold_filter_map_i f acc l =
|
|
let rec aux f acc i map_acc l =
|
|
match l with
|
|
| [] -> acc, List.rev map_acc
|
|
| x :: l' ->
|
|
let acc, y = f acc i x in
|
|
aux f acc (i + 1) (cons_maybe y map_acc) l'
|
|
in
|
|
aux f acc 0 [] l
|
|
|
|
let fold_flat_map f acc l =
|
|
let rec aux f acc map_acc l =
|
|
match l with
|
|
| [] -> acc, List.rev map_acc
|
|
| x :: l' ->
|
|
let acc, y = f acc x in
|
|
aux f acc (List.rev_append y map_acc) l'
|
|
in
|
|
aux f acc [] l
|
|
|
|
let fold_flat_map_i f acc l =
|
|
let rec aux f acc i map_acc l =
|
|
match l with
|
|
| [] -> acc, List.rev map_acc
|
|
| x :: l' ->
|
|
let acc, y = f acc i x in
|
|
aux f acc (i + 1) (List.rev_append y map_acc) l'
|
|
in
|
|
aux f acc 0 [] l
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
(* keep this because it's tailrec for < 5.1 *)
|
|
let init len f =
|
|
let rec indirect_ i acc =
|
|
if i = len then
|
|
List.rev acc
|
|
else (
|
|
let x = f i in
|
|
indirect_ (i + 1) (x :: acc)
|
|
)
|
|
in
|
|
let rec direct_ i =
|
|
if i = len then
|
|
[]
|
|
else if i < direct_depth_default_ then (
|
|
let x = f i in
|
|
x :: direct_ (i + 1)
|
|
) else
|
|
indirect_ i []
|
|
in
|
|
if len < 0 then
|
|
invalid_arg "init"
|
|
else if len = 0 then
|
|
[]
|
|
else
|
|
direct_ 0
|
|
|
|
let rec unfold_kont f seed k =
|
|
match f seed with
|
|
| None -> k []
|
|
| Some (v, next) ->
|
|
let k' tl = k (v :: tl) in
|
|
unfold_kont f next k'
|
|
|
|
let[@inline] unfold f seed =
|
|
let rec direct i f seed =
|
|
if i = 0 then
|
|
unfold_kont f seed (fun x -> x)
|
|
else (
|
|
match f seed with
|
|
| None -> []
|
|
| Some (v, next) -> v :: direct (i - 1) f next
|
|
)
|
|
in
|
|
direct 100 f seed
|
|
|
|
[@@@else_]
|
|
|
|
let[@tail_mod_cons] rec unfold f seed =
|
|
match f seed with
|
|
| None -> []
|
|
| Some (v, next) -> v :: unfold f next
|
|
|
|
[@@@endif]
|
|
|
|
let rec compare f l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> 0
|
|
| _, [] -> 1
|
|
| [], _ -> -1
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let c = f x1 x2 in
|
|
if c <> 0 then
|
|
c
|
|
else
|
|
compare f l1' l2'
|
|
|
|
let rec equal f l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> true
|
|
| [], _ | _, [] -> false
|
|
| x1 :: l1', x2 :: l2' -> f x1 x2 && equal f l1' l2'
|
|
|
|
let rec flat_map_kont f l kont =
|
|
match l with
|
|
| [] -> kont []
|
|
| [ x ] ->
|
|
let x = f x in
|
|
kont x
|
|
| x :: l' ->
|
|
let x = f x in
|
|
let kont' tail = kont (append x tail) in
|
|
flat_map_kont f l' kont'
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
let[@inline] flat_map f l =
|
|
match l with
|
|
| [] -> []
|
|
| [ x ] -> f x
|
|
| _ :: _ -> flat_map_kont f l Fun.id
|
|
|
|
[@@@else_]
|
|
|
|
(* let flat_map = concat_map *)
|
|
let flat_map f l =
|
|
let rec direct i f l =
|
|
match l with
|
|
| [] -> []
|
|
| [ x ] -> f x
|
|
| [ x; y ] ->
|
|
let x = f x in
|
|
let y = f y in
|
|
append x y
|
|
| _ when i = 0 -> flat_map_kont f l Fun.id
|
|
| x :: y :: tl ->
|
|
let x = f x in
|
|
let y = f y in
|
|
let tl = direct (i - 1) f tl in
|
|
append x (append y tl)
|
|
in
|
|
direct 1000 f l
|
|
|
|
[@@@endif]
|
|
|
|
let flat_map_i f l =
|
|
let rec aux f i l kont =
|
|
match l with
|
|
| [] -> kont []
|
|
| x :: l' ->
|
|
let y = f i x in
|
|
let kont' tail =
|
|
match y with
|
|
| [] -> kont tail
|
|
| [ x ] -> kont (x :: tail)
|
|
| [ x; y ] -> kont (x :: y :: tail)
|
|
| l -> kont (append l tail)
|
|
in
|
|
aux f (i + 1) l' kont'
|
|
in
|
|
aux f 0 l (fun l -> l)
|
|
|
|
let flatten l = fold_right append l []
|
|
|
|
let count f l =
|
|
fold_left
|
|
(fun n x ->
|
|
if f x then
|
|
succ n
|
|
else
|
|
n)
|
|
0 l
|
|
|
|
let count_true_false p l =
|
|
fold_left
|
|
(fun (ok, ko) x ->
|
|
if p x then
|
|
ok + 1, ko
|
|
else
|
|
ok, ko + 1)
|
|
(0, 0) l
|
|
|
|
let[@inline] product f l1 l2 = flat_map (fun x -> map (fun y -> f x y) l2) l1
|
|
|
|
let fold_product f acc l1 l2 =
|
|
List.fold_left
|
|
(fun acc x1 -> List.fold_left (fun acc x2 -> f acc x1 x2) acc l2)
|
|
acc l1
|
|
|
|
let diagonal l =
|
|
let rec gen acc l =
|
|
match l with
|
|
| [] -> acc
|
|
| x :: l' ->
|
|
let acc = List.fold_left (fun acc y -> (x, y) :: acc) acc l' in
|
|
gen acc l'
|
|
in
|
|
gen [] l
|
|
|
|
let partition_map_either f l =
|
|
let rec iter f l1 l2 l =
|
|
match l with
|
|
| [] -> List.rev l1, List.rev l2
|
|
| x :: tl ->
|
|
(match f x with
|
|
| CCEither.Left y -> iter f (y :: l1) l2 tl
|
|
| CCEither.Right y -> iter f l1 (y :: l2) tl)
|
|
in
|
|
iter f [] [] l
|
|
|
|
let partition_filter_map f l =
|
|
let rec iter f l1 l2 l =
|
|
match l with
|
|
| [] -> List.rev l1, List.rev l2
|
|
| x :: tl ->
|
|
(match f x with
|
|
| `Left y -> iter f (y :: l1) l2 tl
|
|
| `Right y -> iter f l1 (y :: l2) tl
|
|
| `Drop -> iter f l1 l2 tl)
|
|
in
|
|
iter f [] [] l
|
|
|
|
let partition_map = partition_filter_map
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
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
|
|
|
|
[@@@else_]
|
|
|
|
let[@tail_mod_cons] rec combine l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> []
|
|
| x1 :: l1', x2 :: l2' -> (x1, x2) :: combine l1' l2'
|
|
| _, _ -> invalid_arg "CCList.combine"
|
|
|
|
[@@@endif]
|
|
|
|
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)
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
let combine_shortest 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'
|
|
and safe l1 l2 acc =
|
|
match l1, l2 with
|
|
| [], _ | _, [] -> List.rev acc
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let acc = (x1, x2) :: acc in
|
|
safe l1' l2' acc
|
|
in
|
|
direct direct_depth_default_ l1 l2
|
|
|
|
[@@@else_]
|
|
|
|
let[@tail_mod_cons] rec combine_shortest l1 l2 =
|
|
match l1, l2 with
|
|
| _, [] | [], _ -> []
|
|
| x1 :: l1', x2 :: l2' -> (x1, x2) :: combine_shortest l1' l2'
|
|
|
|
[@@@endif]
|
|
|
|
let split l =
|
|
let rec direct i l =
|
|
match l with
|
|
| [] -> [], []
|
|
| [ (x1, y1) ] -> [ x1 ], [ y1 ]
|
|
| [ (x1, y1); (x2, y2) ] -> [ x1; x2 ], [ y1; y2 ]
|
|
| [ (x1, y1); (x2, y2); (x3, y3) ] -> [ x1; x2; x3 ], [ y1; y2; y3 ]
|
|
| [ (x1, y1); (x2, y2); (x3, y3); (x4, y4) ] ->
|
|
[ x1; x2; x3; x4 ], [ y1; y2; y3; y4 ]
|
|
| _ when i = 0 -> split_slow [] [] l
|
|
| (x1, y1) :: (x2, y2) :: (x3, y3) :: (x4, y4) :: (x5, y5) :: l' ->
|
|
let rx, ry = direct (i - 1) l' in
|
|
x1 :: x2 :: x3 :: x4 :: x5 :: rx, y1 :: y2 :: y3 :: y4 :: y5 :: ry
|
|
and split_slow acc1 acc2 l =
|
|
match l with
|
|
| [] -> List.rev acc1, List.rev acc2
|
|
| (x1, x2) :: tail ->
|
|
let acc1 = x1 :: acc1 and acc2 = x2 :: acc2 in
|
|
split_slow acc1 acc2 tail
|
|
in
|
|
direct direct_depth_default_ l
|
|
|
|
let return x = [ x ]
|
|
let pure = return
|
|
let ( <*> ) funs l = product (fun f x -> f x) funs l
|
|
|
|
let cartesian_product l =
|
|
(* [left]: elements picked so far
|
|
[right]: sets to pick elements from
|
|
[acc]: accumulator for the result, to pass to continuation
|
|
[k]: continuation *)
|
|
let rec prod_rec left right k acc =
|
|
match right with
|
|
| [] -> k acc (List.rev left)
|
|
| l1 :: tail ->
|
|
List.fold_left (fun acc x -> prod_rec (x :: left) tail k acc) acc l1
|
|
in
|
|
prod_rec [] l (fun acc l' -> l' :: acc) []
|
|
|
|
(* cartesian product of lists of lists *)
|
|
let map_product_l f l =
|
|
let l = List.map f l in
|
|
cartesian_product l
|
|
|
|
let rec sorted_mem ~cmp x l =
|
|
match l with
|
|
| [] -> false
|
|
| y :: tail ->
|
|
(match cmp x y with
|
|
| 0 -> true
|
|
| n when n < 0 -> false
|
|
| _ -> (sorted_mem [@tailcall]) ~cmp x tail)
|
|
|
|
let sorted_merge ~cmp l1 l2 =
|
|
let rec recurse cmp acc l1 l2 =
|
|
match l1, l2 with
|
|
| [], _ -> List.rev_append acc l2
|
|
| _, [] -> List.rev_append acc l1
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let c = cmp x1 x2 in
|
|
if c < 0 then
|
|
recurse cmp (x1 :: acc) l1' l2
|
|
else if c > 0 then
|
|
recurse cmp (x2 :: acc) l1 l2'
|
|
else
|
|
recurse cmp (x1 :: x2 :: acc) l1' l2'
|
|
in
|
|
recurse cmp [] l1 l2
|
|
|
|
let sorted_diff ~cmp l1 l2 =
|
|
let rec recurse cmp acc l1 l2 =
|
|
match l1, l2 with
|
|
| [], _ -> List.rev acc
|
|
| _, [] -> List.rev_append acc l1
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let c = cmp x1 x2 in
|
|
if c < 0 then
|
|
recurse cmp (x1 :: acc) l1' l2
|
|
else if c > 0 then
|
|
recurse cmp acc l1 l2'
|
|
else
|
|
recurse cmp acc l1' l2'
|
|
in
|
|
recurse cmp [] l1 l2
|
|
|
|
let sort_uniq ~cmp l = List.sort_uniq cmp l
|
|
|
|
let is_sorted ~cmp l =
|
|
let rec aux cmp = function
|
|
| [] | [ _ ] -> true
|
|
| x :: (y :: _ as tail) -> cmp x y <= 0 && aux cmp tail
|
|
in
|
|
aux cmp l
|
|
|
|
let sorted_insert ~cmp ?(uniq = false) x l =
|
|
let rec aux cmp uniq x left l =
|
|
match l with
|
|
| [] -> List.rev_append left [ x ]
|
|
| y :: tail ->
|
|
(match cmp x y with
|
|
| 0 ->
|
|
let l' =
|
|
if uniq then
|
|
l
|
|
else
|
|
x :: l
|
|
in
|
|
List.rev_append left l'
|
|
| n when n < 0 -> List.rev_append left (x :: l)
|
|
| _ -> aux cmp uniq x (y :: left) tail)
|
|
in
|
|
aux cmp uniq x [] l
|
|
|
|
let sorted_remove ~cmp ?(all = false) x l =
|
|
let rec aux cmp all x left l =
|
|
match l with
|
|
| [] -> List.rev left
|
|
| y :: tail ->
|
|
(match cmp x y with
|
|
| 0 ->
|
|
if all then
|
|
aux cmp all x left tail
|
|
else
|
|
List.rev_append left tail
|
|
| n when n < 0 -> List.rev_append left l
|
|
| _ -> aux cmp all x (y :: left) tail)
|
|
in
|
|
aux cmp all x [] l
|
|
|
|
let uniq_succ ~eq l =
|
|
let rec f acc l =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| [ x ] -> List.rev (x :: acc)
|
|
| x :: (y :: _ as tail) when eq x y -> f acc tail
|
|
| x :: tail -> f (x :: acc) tail
|
|
in
|
|
f [] l
|
|
|
|
let group_succ ~eq l =
|
|
let rec f ~eq acc cur l =
|
|
match cur, l with
|
|
| [], [] -> List.rev acc
|
|
| _ :: _, [] -> List.rev (List.rev cur :: acc)
|
|
| [], x :: tl -> f ~eq acc [ x ] tl
|
|
| y :: _, x :: tl when eq x y -> f ~eq acc (x :: cur) tl
|
|
| _, x :: tl -> f ~eq (List.rev cur :: acc) [ x ] tl
|
|
in
|
|
f ~eq [] [] l
|
|
|
|
let sorted_merge_uniq ~cmp l1 l2 =
|
|
let push ~cmp acc x =
|
|
match acc with
|
|
| [] -> [ x ]
|
|
| y :: _ when cmp x y > 0 -> x :: acc
|
|
| _ -> acc
|
|
(* duplicate, do not yield *)
|
|
in
|
|
let rec recurse ~cmp acc l1 l2 =
|
|
match l1, l2 with
|
|
| [], l | l, [] ->
|
|
let acc = List.fold_left (push ~cmp) acc l in
|
|
List.rev acc
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let c = cmp x1 x2 in
|
|
if c < 0 then
|
|
recurse ~cmp (push ~cmp acc x1) l1' l2
|
|
else if c > 0 then
|
|
recurse ~cmp (push ~cmp acc x2) l1 l2'
|
|
else
|
|
recurse ~cmp acc l1 l2'
|
|
(* drop one of the [x] *)
|
|
in
|
|
recurse ~cmp [] l1 l2
|
|
|
|
let sorted_diff_uniq ~cmp l1 l2 =
|
|
let push ~cmp acc x =
|
|
match acc with
|
|
| [] -> [ x ]
|
|
| y :: _ when cmp x y > 0 -> x :: acc
|
|
| _ -> acc
|
|
(* duplicate, do not yield *)
|
|
in
|
|
let rec recurse ~cmp acc l1 l2 =
|
|
match l1, l2 with
|
|
| [], _ -> List.rev acc
|
|
| l, [] ->
|
|
let acc = List.fold_left (push ~cmp) acc l in
|
|
List.rev acc
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let c = cmp x1 x2 in
|
|
if c < 0 then
|
|
recurse ~cmp (push ~cmp acc x1) l1' l2
|
|
else if c > 0 then
|
|
recurse ~cmp acc l1 l2'
|
|
else
|
|
recurse ~cmp acc l1' l2'
|
|
in
|
|
recurse ~cmp [] l1 l2
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
let take n l =
|
|
let rec direct i n l =
|
|
match l with
|
|
| [] -> []
|
|
| _ when i = 0 -> safe n [] l
|
|
| x :: l' ->
|
|
if n > 0 then
|
|
x :: direct (i - 1) (n - 1) l'
|
|
else
|
|
[]
|
|
and safe n acc l =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| _ when n = 0 -> List.rev acc
|
|
| x :: l' -> safe (n - 1) (x :: acc) l'
|
|
in
|
|
direct direct_depth_default_ n l
|
|
|
|
[@@@else_]
|
|
|
|
let[@tail_mod_cons] rec take n l =
|
|
match l with
|
|
| [] -> []
|
|
| x :: l' ->
|
|
if n > 0 then
|
|
x :: take (n - 1) l'
|
|
else
|
|
[]
|
|
|
|
[@@@endif]
|
|
|
|
let rec drop n l =
|
|
match l with
|
|
| [] -> []
|
|
| _ when n = 0 -> l
|
|
| _ :: l' -> drop (n - 1) l'
|
|
|
|
let hd_tl = function
|
|
| [] -> failwith "hd_tl"
|
|
| x :: l -> x, l
|
|
|
|
let take_drop n l = take n l, drop n l
|
|
|
|
let sublists_of_len ?(last = fun _ -> None) ?offset n l =
|
|
if n < 1 then invalid_arg "sublists_of_len: n must be > 0";
|
|
let offset =
|
|
match offset with
|
|
| None -> n
|
|
| Some o when o < 1 -> invalid_arg "sublists_of_len: offset must be > 0"
|
|
| Some o -> o
|
|
in
|
|
(* add sub-lists of [l] to [acc] *)
|
|
let rec aux acc l =
|
|
let group = take n l in
|
|
if is_empty group then
|
|
acc
|
|
(* this was the last group, we are done *)
|
|
else if List.length group < n (* last group, with missing elements *) then (
|
|
match last group with
|
|
| None -> acc
|
|
| Some group' -> group' :: acc
|
|
) else (
|
|
let l' = drop offset l in
|
|
aux (group :: acc) l' (* continue *)
|
|
)
|
|
in
|
|
List.rev (aux [] l)
|
|
|
|
let chunks n l = sublists_of_len ~last:(fun x -> Some x) n l
|
|
|
|
let intersperse x l =
|
|
let rec aux_direct i x l =
|
|
match l with
|
|
| [] -> []
|
|
| [ _ ] -> l
|
|
| _ when i = 0 -> aux_tailrec [] x l
|
|
| y :: tail -> y :: x :: aux_direct (i - 1) x tail
|
|
and aux_tailrec acc x l =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| [ y ] -> List.rev (y :: acc)
|
|
| y :: tail -> aux_tailrec (x :: y :: acc) x tail
|
|
in
|
|
aux_direct 1_000 x l
|
|
|
|
let interleave l1 l2 : _ list =
|
|
let rec aux acc l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> List.rev acc
|
|
| [], _ -> List.rev (List.rev_append l2 acc)
|
|
| _, [] -> List.rev (List.rev_append l1 acc)
|
|
| x1 :: tl1, x2 :: tl2 -> aux (x2 :: x1 :: acc) tl1 tl2
|
|
in
|
|
aux [] l1 l2
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
let take_while p l =
|
|
let rec direct i p l =
|
|
match l with
|
|
| [] -> []
|
|
| _ when i = 0 -> safe p [] l
|
|
| x :: l' ->
|
|
if p x then
|
|
x :: direct (i - 1) p l'
|
|
else
|
|
[]
|
|
and safe p acc l =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| x :: l' ->
|
|
if p x then
|
|
safe p (x :: acc) l'
|
|
else
|
|
List.rev acc
|
|
in
|
|
direct direct_depth_default_ p l
|
|
|
|
[@@@else_]
|
|
|
|
let rec take_while p l =
|
|
match l with
|
|
| [] -> []
|
|
| x :: l' ->
|
|
if p x then
|
|
x :: take_while p l'
|
|
else
|
|
[]
|
|
|
|
[@@@endif]
|
|
|
|
let rec drop_while p l =
|
|
match l with
|
|
| [] -> []
|
|
| x :: l' ->
|
|
if p x then
|
|
drop_while p l'
|
|
else
|
|
l
|
|
|
|
let take_drop_while p l =
|
|
let rec direct i p l =
|
|
match l with
|
|
| [] -> [], []
|
|
| _ when i = 0 -> safe p [] l
|
|
| x :: tail ->
|
|
if p x then (
|
|
let l1, l2 = direct (i - 1) p tail in
|
|
x :: l1, l2
|
|
) else
|
|
[], l
|
|
and safe p acc l =
|
|
match l with
|
|
| [] -> List.rev acc, []
|
|
| x :: tail ->
|
|
if p x then
|
|
safe p (x :: acc) tail
|
|
else
|
|
List.rev acc, l
|
|
in
|
|
direct direct_depth_default_ p l
|
|
|
|
let last n l =
|
|
let len = List.length l in
|
|
if len < n then
|
|
l
|
|
else
|
|
drop (len - n) l
|
|
|
|
let head_opt = function
|
|
| [] -> None
|
|
| x :: _ -> Some x
|
|
|
|
let tail_opt = function
|
|
| [] -> None
|
|
| _ :: tail -> Some tail
|
|
|
|
let rec last_opt = function
|
|
| [] -> None
|
|
| [ x ] -> Some x
|
|
| _ :: tail -> last_opt tail
|
|
|
|
let find_pred = find_opt
|
|
|
|
let find_pred_exn p l =
|
|
match find_pred p l with
|
|
| None -> raise Not_found
|
|
| Some x -> x
|
|
|
|
let find_mapi f l =
|
|
let rec aux f i = function
|
|
| [] -> None
|
|
| x :: l' ->
|
|
(match f i x with
|
|
| Some _ as res -> res
|
|
| None -> aux f (i + 1) l')
|
|
in
|
|
aux f 0 l
|
|
|
|
let find_map f l = find_mapi (fun _ -> f) l
|
|
|
|
let find_idx p l =
|
|
find_mapi
|
|
(fun i x ->
|
|
if p x then
|
|
Some (i, x)
|
|
else
|
|
None)
|
|
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
|
|
| y :: tail -> remove' eq x (y :: acc) tail
|
|
in
|
|
remove' eq x [] l
|
|
|
|
let filter_map f l =
|
|
let rec recurse acc l =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| x :: l' ->
|
|
let acc' =
|
|
match f x with
|
|
| None -> acc
|
|
| Some y -> y :: acc
|
|
in
|
|
recurse acc' l'
|
|
in
|
|
recurse [] l
|
|
|
|
let keep_some l = filter_map (fun x -> x) l
|
|
|
|
let keep_ok l =
|
|
filter_map
|
|
(function
|
|
| Ok x -> Some x
|
|
| Error _ -> None)
|
|
l
|
|
|
|
let all_some l =
|
|
try
|
|
Some
|
|
(map
|
|
(function
|
|
| Some x -> x
|
|
| None -> raise Exit)
|
|
l)
|
|
with Exit -> None
|
|
|
|
let all_ok l =
|
|
let err = ref None in
|
|
try
|
|
Ok
|
|
(map
|
|
(function
|
|
| Ok x -> x
|
|
| Error e ->
|
|
err := Some e;
|
|
raise Exit)
|
|
l)
|
|
with Exit ->
|
|
(match !err with
|
|
| None -> assert false
|
|
| Some e -> Error e)
|
|
|
|
let group_by (type k) ?(hash = Hashtbl.hash) ?(eq = Stdlib.( = )) l =
|
|
let module Tbl = Hashtbl.Make (struct
|
|
type t = k
|
|
|
|
let equal = eq
|
|
let hash = hash
|
|
end) in
|
|
(* compute group table *)
|
|
let tbl = Tbl.create 32 in
|
|
List.iter
|
|
(fun x ->
|
|
let l = try Tbl.find tbl x with Not_found -> [] in
|
|
Tbl.replace tbl x (x :: l))
|
|
l;
|
|
Tbl.fold (fun _ x acc -> x :: acc) tbl []
|
|
|
|
let join ~join_row s1 s2 : _ t =
|
|
flat_map (fun a -> filter_map (join_row a) s2) s1
|
|
|
|
let join_by (type a) ?(eq = Stdlib.( = )) ?(hash = Hashtbl.hash) f1 f2 ~merge c1
|
|
c2 =
|
|
let module Tbl = Hashtbl.Make (struct
|
|
type t = a
|
|
|
|
let equal = eq
|
|
let hash = hash
|
|
end) in
|
|
let tbl = Tbl.create 32 in
|
|
List.iter
|
|
(fun x ->
|
|
let key = f1 x in
|
|
Tbl.add tbl key x)
|
|
c1;
|
|
let res = ref [] in
|
|
List.iter
|
|
(fun y ->
|
|
let key = f2 y in
|
|
let xs = Tbl.find_all tbl key in
|
|
List.iter
|
|
(fun x ->
|
|
match merge key x y with
|
|
| None -> ()
|
|
| Some z -> res := z :: !res)
|
|
xs)
|
|
c2;
|
|
!res
|
|
|
|
type ('a, 'b) join_all_cell = {
|
|
mutable ja_left: 'a list;
|
|
mutable ja_right: 'b list;
|
|
}
|
|
|
|
let join_all_by (type a) ?(eq = Stdlib.( = )) ?(hash = Hashtbl.hash) f1 f2
|
|
~merge c1 c2 =
|
|
let module Tbl = Hashtbl.Make (struct
|
|
type t = a
|
|
|
|
let equal = eq
|
|
let hash = hash
|
|
end) in
|
|
let tbl = Tbl.create 32 in
|
|
(* build the map [key -> cell] *)
|
|
List.iter
|
|
(fun x ->
|
|
let key = f1 x in
|
|
try
|
|
let c = Tbl.find tbl key in
|
|
c.ja_left <- x :: c.ja_left
|
|
with Not_found -> Tbl.add tbl key { ja_left = [ x ]; ja_right = [] })
|
|
c1;
|
|
List.iter
|
|
(fun y ->
|
|
let key = f2 y in
|
|
try
|
|
let c = Tbl.find tbl key in
|
|
c.ja_right <- y :: c.ja_right
|
|
with Not_found -> Tbl.add tbl key { ja_left = []; ja_right = [ y ] })
|
|
c2;
|
|
Tbl.fold
|
|
(fun key cell res ->
|
|
match merge key cell.ja_left cell.ja_right with
|
|
| None -> res
|
|
| Some z -> z :: res)
|
|
tbl []
|
|
|
|
let group_join_by (type a) ?(eq = Stdlib.( = )) ?(hash = Hashtbl.hash) f c1 c2 =
|
|
let module Tbl = Hashtbl.Make (struct
|
|
type t = a
|
|
|
|
let equal = eq
|
|
let hash = hash
|
|
end) in
|
|
let tbl = Tbl.create 32 in
|
|
List.iter (fun x -> Tbl.replace tbl x []) c1;
|
|
List.iter
|
|
(fun y ->
|
|
(* project [y] into some element of [c1] *)
|
|
let key = f y in
|
|
try
|
|
let l = Tbl.find tbl key in
|
|
Tbl.replace tbl key (y :: l)
|
|
with Not_found -> ())
|
|
c2;
|
|
Tbl.fold (fun k v l -> (k, v) :: l) tbl []
|
|
|
|
let mem ?(eq = Stdlib.( = )) x l =
|
|
let rec search eq x l =
|
|
match l with
|
|
| [] -> false
|
|
| y :: l' -> eq x y || search eq x l'
|
|
in
|
|
search eq x l
|
|
|
|
let add_nodup ~eq x l =
|
|
if mem ~eq x l then
|
|
l
|
|
else
|
|
x :: l
|
|
|
|
let remove_one ~eq x l =
|
|
let rec remove_one ~eq x acc l =
|
|
match l with
|
|
| [] -> assert false
|
|
| y :: tl when eq x y -> List.rev_append acc tl
|
|
| y :: tl -> remove_one ~eq x (y :: acc) tl
|
|
in
|
|
if mem ~eq x l then
|
|
remove_one ~eq x [] l
|
|
else
|
|
l
|
|
|
|
let subset ~eq l1 l2 = List.for_all (fun t -> mem ~eq t l2) l1
|
|
|
|
let uniq ~eq l =
|
|
let rec uniq eq acc l =
|
|
match l with
|
|
| [] -> List.rev acc
|
|
| x :: xs when List.exists (eq x) xs -> uniq eq acc xs
|
|
| x :: xs -> uniq eq (x :: acc) xs
|
|
in
|
|
uniq eq [] l
|
|
|
|
let union ~eq l1 l2 =
|
|
let rec union eq acc l1 l2 =
|
|
match l1 with
|
|
| [] -> List.rev_append acc l2
|
|
| x :: xs when mem ~eq x l2 -> union eq acc xs l2
|
|
| x :: xs -> union eq (x :: acc) xs l2
|
|
in
|
|
union eq [] l1 l2
|
|
|
|
let inter ~eq l1 l2 =
|
|
let rec inter eq acc l1 l2 =
|
|
match l1 with
|
|
| [] -> List.rev acc
|
|
| x :: xs when mem ~eq x l2 -> inter eq (x :: acc) xs l2
|
|
| _ :: xs -> inter eq acc xs l2
|
|
in
|
|
inter eq [] l1 l2
|
|
|
|
let mapi f l =
|
|
let r = ref 0 in
|
|
map
|
|
(fun x ->
|
|
let y = f !r x in
|
|
incr r;
|
|
y)
|
|
l
|
|
|
|
let iteri f l =
|
|
let rec aux f i l =
|
|
match l with
|
|
| [] -> ()
|
|
| x :: l' ->
|
|
f i x;
|
|
aux f (i + 1) l'
|
|
in
|
|
aux f 0 l
|
|
|
|
let iteri2 f l1 l2 =
|
|
let rec aux f i l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> ()
|
|
| [], _ | _, [] -> invalid_arg "iteri2"
|
|
| x1 :: l1', x2 :: l2' ->
|
|
f i x1 x2;
|
|
aux f (i + 1) l1' l2'
|
|
in
|
|
aux f 0 l1 l2
|
|
|
|
let foldi f acc l =
|
|
let rec foldi f acc i l =
|
|
match l with
|
|
| [] -> acc
|
|
| x :: l' ->
|
|
let acc = f acc i x in
|
|
foldi f acc (i + 1) l'
|
|
in
|
|
foldi f acc 0 l
|
|
|
|
let foldi2 f acc l1 l2 =
|
|
let rec foldi f acc i l1 l2 =
|
|
match l1, l2 with
|
|
| [], [] -> acc
|
|
| [], _ | _, [] -> invalid_arg "foldi2"
|
|
| x1 :: l1', x2 :: l2' ->
|
|
let acc = f acc i x1 x2 in
|
|
foldi f acc (i + 1) l1' l2'
|
|
in
|
|
foldi f acc 0 l1 l2
|
|
|
|
let rec get_at_idx_rec i l =
|
|
match l with
|
|
| [] -> raise Not_found
|
|
| x :: _ when i = 0 -> x
|
|
| _ :: l' -> get_at_idx_rec (i - 1) l'
|
|
|
|
let get_at_idx_exn i l =
|
|
let i =
|
|
if i < 0 then
|
|
length l + i
|
|
else
|
|
i
|
|
in
|
|
get_at_idx_rec i l
|
|
|
|
let get_at_idx i l = try Some (get_at_idx_exn i l) with Not_found -> None
|
|
|
|
let set_at_idx i x l0 =
|
|
let rec aux l acc i =
|
|
match l with
|
|
| [] -> l0
|
|
| _ :: l' when i = 0 -> List.rev_append acc (x :: l')
|
|
| y :: l' -> aux l' (y :: acc) (i - 1)
|
|
in
|
|
let i =
|
|
if i < 0 then
|
|
length l0 + i
|
|
else
|
|
i
|
|
in
|
|
aux l0 [] i
|
|
|
|
let insert_at_idx i x l =
|
|
let rec aux l acc i x =
|
|
match l with
|
|
| [] -> List.rev_append acc [ x ]
|
|
| y :: l' when i = 0 -> List.rev_append acc (x :: y :: l')
|
|
| y :: l' -> aux l' (y :: acc) (i - 1) x
|
|
in
|
|
let i =
|
|
if i < 0 then
|
|
length l + i
|
|
else
|
|
i
|
|
in
|
|
aux l [] i x
|
|
|
|
let remove_at_idx i l0 =
|
|
let rec aux l acc i =
|
|
match l with
|
|
| [] -> l0
|
|
| _ :: l' when i = 0 -> List.rev_append acc l'
|
|
| y :: l' -> aux l' (y :: acc) (i - 1)
|
|
in
|
|
let i =
|
|
if i < 0 then
|
|
length l0 + i
|
|
else
|
|
i
|
|
in
|
|
aux l0 [] i
|
|
|
|
let range_by ~step i j =
|
|
let rec range i j acc =
|
|
if i = j then
|
|
i :: acc
|
|
else
|
|
range i (j - step) (j :: acc)
|
|
in
|
|
if step = 0 then
|
|
raise (Invalid_argument "CCList.range_by")
|
|
else if
|
|
if step > 0 then
|
|
i > j
|
|
else
|
|
i < j
|
|
then
|
|
[]
|
|
else
|
|
range i (((j - i) / step * step) + i) []
|
|
|
|
let range i j =
|
|
let rec up i j acc =
|
|
if i = j then
|
|
i :: acc
|
|
else
|
|
up i (j - 1) (j :: acc)
|
|
and down i j acc =
|
|
if i = j then
|
|
i :: acc
|
|
else
|
|
down i (j + 1) (j :: acc)
|
|
in
|
|
if i <= j then
|
|
up i j []
|
|
else
|
|
down i j []
|
|
|
|
let range' i j =
|
|
if i < j then
|
|
range i (j - 1)
|
|
else if i = j then
|
|
[]
|
|
else
|
|
range i (j + 1)
|
|
|
|
let ( -- ) = range
|
|
let ( --^ ) = range'
|
|
|
|
let replicate i x =
|
|
let rec aux acc i =
|
|
if i = 0 then
|
|
acc
|
|
else
|
|
aux (x :: acc) (i - 1)
|
|
in
|
|
aux [] i
|
|
|
|
let repeat i l =
|
|
let rec aux acc i =
|
|
if i = 0 then
|
|
List.rev acc
|
|
else
|
|
aux (List.rev_append l acc) (i - 1)
|
|
in
|
|
aux [] i
|
|
|
|
module Assoc = struct
|
|
type ('a, 'b) t = ('a * 'b) list
|
|
|
|
let rec search_exn eq l x =
|
|
match l with
|
|
| [] -> raise Not_found
|
|
| (y, z) :: l' ->
|
|
if eq x y then
|
|
z
|
|
else
|
|
search_exn eq l' x
|
|
|
|
let get_exn ~eq x l = search_exn eq l x
|
|
let get ~eq x l = try Some (search_exn eq l x) with Not_found -> None
|
|
|
|
(* search for a binding for [x] in [l], and calls [f x (Some v) rest]
|
|
or [f x None rest] depending on whether it finds the binding.
|
|
[rest] is the list of the other bindings *)
|
|
let rec search_set eq acc l x ~f =
|
|
match l with
|
|
| [] -> f x None acc
|
|
| (x', y') :: l' ->
|
|
if eq x x' then
|
|
f x (Some y') (List.rev_append acc l')
|
|
else
|
|
search_set eq ((x', y') :: acc) l' x ~f
|
|
|
|
let set ~eq x y l = search_set eq [] l x ~f:(fun x _ l -> (x, y) :: l)
|
|
|
|
let mem ?(eq = Stdlib.( = )) x l =
|
|
try
|
|
ignore (search_exn eq l x);
|
|
true
|
|
with Not_found -> false
|
|
|
|
let update ~eq f x l =
|
|
search_set eq [] l x ~f:(fun x opt_y rest ->
|
|
match f opt_y with
|
|
| None -> rest (* drop *)
|
|
| Some y' -> (x, y') :: rest)
|
|
|
|
let remove ~eq x l =
|
|
search_set eq [] l x ~f:(fun _ opt_y rest ->
|
|
match opt_y with
|
|
| None -> l (* keep as is *)
|
|
| Some _ -> rest)
|
|
|
|
let keys l = map (fun (k, _) -> k) l
|
|
let values l = map (fun (_, v) -> v) l
|
|
let map_values f l = map (fun (k, v) -> k, f v) l
|
|
end
|
|
|
|
let assoc = Assoc.get_exn
|
|
let assoc_opt = Assoc.get
|
|
let mem_assoc = Assoc.mem
|
|
let remove_assoc = Assoc.remove
|
|
|
|
(** {2 References on Lists} *)
|
|
|
|
module Ref = struct
|
|
type 'a t = 'a list ref
|
|
|
|
let push l x = l := x :: !l
|
|
|
|
let pop l =
|
|
match !l with
|
|
| [] -> None
|
|
| x :: tail ->
|
|
l := tail;
|
|
Some x
|
|
|
|
let pop_exn l =
|
|
match !l with
|
|
| [] -> failwith "CCList.Ref.pop_exn"
|
|
| x :: tail ->
|
|
l := tail;
|
|
x
|
|
|
|
let create () = ref []
|
|
let clear l = l := []
|
|
let lift f l = f !l
|
|
let push_list r l = r := List.rev_append l !r
|
|
end
|
|
|
|
(** {2 Monadic Operations} *)
|
|
module type MONAD = sig
|
|
type 'a t
|
|
|
|
val return : 'a -> 'a t
|
|
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
|
|
end
|
|
|
|
module Traverse (M : MONAD) = struct
|
|
open M
|
|
|
|
let map_m f l =
|
|
let rec aux f acc l =
|
|
match l with
|
|
| [] -> return (List.rev acc)
|
|
| x :: tail -> f x >>= fun x' -> aux f (x' :: acc) tail
|
|
in
|
|
aux f [] l
|
|
|
|
let rec map_m_par f l =
|
|
match l with
|
|
| [] -> M.return []
|
|
| x :: tl ->
|
|
let x' = f x in
|
|
let tl' = map_m_par f tl in
|
|
x' >>= fun x' ->
|
|
tl' >>= fun tl' -> M.return (x' :: tl')
|
|
|
|
let sequence_m l = map_m (fun x -> x) l
|
|
|
|
let rec fold_m f acc l =
|
|
match l with
|
|
| [] -> return acc
|
|
| x :: l' -> f acc x >>= fun acc' -> fold_m f acc' l'
|
|
end
|
|
|
|
(** {2 Conversions} *)
|
|
|
|
type 'a iter = ('a -> unit) -> unit
|
|
type 'a gen = unit -> 'a option
|
|
type 'a printer = Format.formatter -> 'a -> unit
|
|
type 'a random_gen = Random.State.t -> 'a
|
|
|
|
let random_len len g st = init len (fun _ -> g st)
|
|
|
|
let random g st =
|
|
let len = Random.State.int st 1_000 in
|
|
random_len len g st
|
|
|
|
let random_non_empty g st =
|
|
let len = 1 + Random.State.int st 1_000 in
|
|
random_len len g st
|
|
|
|
let random_choose l =
|
|
match l with
|
|
| [] -> raise Not_found
|
|
| _ :: _ ->
|
|
let len = List.length l in
|
|
fun st ->
|
|
let i = Random.State.int st len in
|
|
List.nth l i
|
|
|
|
let random_sequence l st = map (fun g -> g st) l
|
|
|
|
let to_string ?(start = "") ?(stop = "") ?(sep = ", ") item_to_string l =
|
|
let l = List.map item_to_string l in
|
|
start ^ String.concat sep l ^ stop
|
|
|
|
let to_iter l k = List.iter k l
|
|
|
|
let rec to_seq l () =
|
|
match l with
|
|
| [] -> Seq.Nil
|
|
| x :: tl -> Seq.Cons (x, to_seq tl)
|
|
|
|
let of_iter i =
|
|
let l = ref [] in
|
|
i (fun x -> l := x :: !l);
|
|
List.rev !l
|
|
|
|
let of_seq_rev l =
|
|
let rec loop acc s =
|
|
match s () with
|
|
| Seq.Nil -> acc
|
|
| Seq.Cons (x, tl) -> loop (x :: acc) tl
|
|
in
|
|
loop [] l
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
let of_seq l =
|
|
let rec direct i seq =
|
|
if i <= 0 then
|
|
List.rev (of_seq_rev seq)
|
|
else (
|
|
match seq () with
|
|
| Seq.Nil -> []
|
|
| Seq.Cons (x, tl) -> x :: direct (i - 1) tl
|
|
)
|
|
in
|
|
direct direct_depth_default_ l
|
|
|
|
[@@@endif]
|
|
|
|
let to_gen l =
|
|
let l = ref l in
|
|
fun () ->
|
|
match !l with
|
|
| [] -> None
|
|
| x :: l' ->
|
|
l := l';
|
|
Some x
|
|
|
|
[@@@iflt 5.1]
|
|
|
|
let of_gen g =
|
|
let rec direct i g =
|
|
if i = 0 then
|
|
safe [] g
|
|
else (
|
|
match g () with
|
|
| None -> []
|
|
| Some x -> x :: direct (i - 1) g
|
|
)
|
|
and safe acc g =
|
|
match g () with
|
|
| None -> List.rev acc
|
|
| Some x -> safe (x :: acc) g
|
|
in
|
|
direct direct_depth_default_ g
|
|
|
|
[@@@else_]
|
|
|
|
let[@tail_mod_cons] rec of_gen g =
|
|
match g () with
|
|
| None -> []
|
|
| Some x -> x :: of_gen g
|
|
|
|
[@@@endif]
|
|
|
|
module Infix = struct
|
|
let[@inline] ( >|= ) l f = map f l
|
|
let[@inline] ( >>= ) l f = flat_map f l
|
|
let ( @ ) = ( @ )
|
|
let ( <*> ) = ( <*> )
|
|
let ( <$> ) = map
|
|
let ( -- ) = ( -- )
|
|
let ( --^ ) = ( --^ )
|
|
let ( let+ ) = ( >|= )
|
|
let ( let* ) = ( >>= )
|
|
let[@inline] ( and+ ) l1 l2 = product (fun x y -> x, y) l1 l2
|
|
let ( and* ) = ( and+ )
|
|
let ( and& ) = combine_shortest
|
|
end
|
|
|
|
include Infix
|
|
|
|
(** {2 IO} *)
|
|
|
|
let pp ?(pp_start = fun _ () -> ()) ?(pp_stop = fun _ () -> ())
|
|
?(pp_sep = fun fmt () -> Format.fprintf fmt ",@ ") pp_item fmt l =
|
|
let rec print fmt l =
|
|
match l with
|
|
| x :: (_ :: _ as l) ->
|
|
pp_item fmt x;
|
|
pp_sep fmt ();
|
|
print fmt l
|
|
| [ x ] -> pp_item fmt x
|
|
| [] -> ()
|
|
in
|
|
pp_start fmt ();
|
|
print fmt l;
|
|
pp_stop fmt ()
|