ocaml-containers/src/data/CCImmutArray.ml

171 lines
3.9 KiB
OCaml

(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Immutable Arrays} *)
(* TODO: transient API? for batch modifications *)
(*$inject let print_array f a = to_list a |> Array.of_list |> Q.Print.(array f)
*)
type 'a t = 'a array
let empty = [| |]
let length = Array.length
let singleton x = [| x |]
let doubleton x y = [| x; y |]
let make n x = Array.make n x
let init n f = Array.init n f
let get = Array.get
let set a n x =
let a' = Array.copy a in
a'.(n) <- x;
a'
(*$= set & ~printer:(print_array Q.Print.int)
(of_list [0]) (set (of_list [5]) 0 0)
(of_list [1; 3; 4; 5]) (set (of_list [1; 2; 4; 5]) 1 3)
*)
let sub = Array.sub (* Would this not be better implemented with CCArray_slice *)
let map = Array.map
let mapi = Array.mapi
let append a b =
let na = length a in
Array.init (na + length b)
(fun i -> if i < na then a.(i) else b.(i-na))
(*$= append & ~printer:(print_array Q.Print.int)
empty (append empty empty)
(of_list [1; 2; 3]) (append empty (of_list [1; 2; 3]))
(of_list [1; 2; 3]) (append (of_list [1; 2; 3]) empty)
(of_list [3; 1; 4; 1; 5]) (append (of_list [3; 1]) (of_list [4; 1; 5]))
*)
let iter = Array.iter
let iteri = Array.iteri
let fold = Array.fold_left
let foldi f acc a =
let n = ref 0 in
Array.fold_left
(fun acc x ->
let acc = f acc !n x in
incr n;
acc)
acc a
(*$= foldi & ~printer:Q.Print.(list (pair int string))
([2, "baz"; 1, "bar"; 0, "foo"]) (foldi (fun l i a -> (i, a) :: l) [] (of_list ["foo"; "bar"; "baz"]))
*)
exception ExitNow
let for_all p a =
try
Array.iter (fun x -> if not (p x) then raise ExitNow) a;
true
with ExitNow -> false
(*$= for_all & ~printer:Q.Print.bool
true (for_all (fun _ -> false) empty)
false (for_all (fun _ -> false) (singleton 3))
true (for_all (fun n -> n mod 2 = 0) (of_list [2; 4; 8]))
false (for_all (fun n -> n mod 2 = 0) (of_list [2; 4; 5; 8]))
*)
let exists p a =
try
Array.iter (fun x -> if p x then raise ExitNow) a;
false
with ExitNow -> true
(*$= exists & ~printer:Q.Print.bool
false (exists (fun _ -> true) empty)
true (exists (fun _ -> true) (singleton 3))
false (exists (fun _ -> false) (singleton 3))
false (exists (fun n -> n mod 2 = 1) (of_list [2; 4; 8]))
true (exists (fun n -> n mod 2 = 1) (of_list [2; 4; 5; 8]))
*)
(*$Q
Q.(list bool) (fun l -> let a = of_list l in not @@ exists (fun b -> b) a = for_all not a)
Q.(list bool) (fun l -> let a = of_list l in not @@ for_all (fun b -> b) a = exists not a)
*)
(*$Q
Q.(list bool) (fun l -> exists (fun b -> b) (of_list l) = List.fold_left (||) false l)
Q.(list bool) (fun l -> for_all (fun b -> b) (of_list l) = List.fold_left (&&) true l)
*)
(** {2 Conversions} *)
type 'a iter = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
let of_list = Array.of_list
let to_list = Array.to_list
let of_array_unsafe a = a (* careful with that axe, Eugene *)
let to_iter a k = iter k a
let of_iter s =
let l = ref [] in
s (fun x -> l := x :: !l);
Array.of_list (List.rev !l)
(*$Q
Q.(list int) (fun l -> \
let g = Iter.of_list l in \
of_iter g |> to_iter |> Iter.to_list = l)
*)
let rec gen_to_list_ acc g = match g() with
| None -> List.rev acc
| Some x -> gen_to_list_ (x::acc) g
let of_gen g =
let l = gen_to_list_ [] g in
Array.of_list l
let to_gen a =
let i = ref 0 in
fun () ->
if !i < Array.length a then (
let x = a.(!i) in
incr i;
Some x
) else None
(*$Q
Q.(list int) (fun l -> \
let g = Gen.of_list l in \
of_gen g |> to_gen |> Gen.to_list = l)
*)
(** {2 IO} *)
type 'a printer = Format.formatter -> 'a -> unit
let pp ?(pp_start=fun _ () -> ()) ?(pp_stop=fun _ () -> ())
?(pp_sep=fun out () -> Format.fprintf out ",@ ") pp_item out a =
pp_start out ();
for k = 0 to Array.length a - 1 do
if k > 0 then pp_sep out ();
pp_item out a.(k)
done;
pp_stop out ()