From 574b4ac62e0a2af92c9fea80c11d2992178d756d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 1 Mar 2016 20:31:53 +0100 Subject: [PATCH] add `CCImmutArray` into containers.data --- README.adoc | 1 + _oasis | 3 +- doc/intro.txt | 1 + src/data/CCImmutArray.ml | 129 ++++++++++++++++++++++++++++++++++++++ src/data/CCImmutArray.mli | 85 +++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/data/CCImmutArray.ml create mode 100644 src/data/CCImmutArray.mli diff --git a/README.adoc b/README.adoc index e318973e..5e301353 100644 --- a/README.adoc +++ b/README.adoc @@ -165,6 +165,7 @@ Documentation http://cedeela.fr/~simon/software/containers[here]. - `CCWBTree`, a weight-balanced tree, implementing a map interface - `CCRAL`, a random-access list structure, with `O(1)` cons/hd/tl and `O(ln(n))` access to elements by their index. +- `CCImmutArray`, immutable interface to arrays === Containers.io diff --git a/_oasis b/_oasis index bc9f6985..4ce73dd4 100644 --- a/_oasis +++ b/_oasis @@ -77,7 +77,8 @@ Library "containers_data" CCPersistentHashtbl, CCDeque, CCFQueue, CCBV, CCMixtbl, CCMixmap, CCRingBuffer, CCIntMap, CCPersistentArray, CCMixset, CCHashconsedSet, CCGraph, CCHashSet, CCBitField, - CCHashTrie, CCBloom, CCWBTree, CCRAL, CCAllocCache + CCHashTrie, CCBloom, CCWBTree, CCRAL, CCAllocCache, + CCImmutArray BuildDepends: bytes # BuildDepends: bytes, bisect_ppx FindlibParent: containers diff --git a/doc/intro.txt b/doc/intro.txt index 1b331182..7d70eee8 100644 --- a/doc/intro.txt +++ b/doc/intro.txt @@ -75,6 +75,7 @@ CCFQueue CCFlatHashtbl CCHashSet CCHashTrie +CCImmutArray CCIntMap CCMixmap CCMixset diff --git a/src/data/CCImmutArray.ml b/src/data/CCImmutArray.ml new file mode 100644 index 00000000..a775a586 --- /dev/null +++ b/src/data/CCImmutArray.ml @@ -0,0 +1,129 @@ + +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Immutable Arrays} *) + +(* TODO: tests *) +(* TODO: transient API? for batch modifications *) + +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' + +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)) + +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 + +exception ExitNow + +let for_all p a = + try + Array.iter (fun x -> if not (p x) then raise ExitNow) a; + true + with ExitNow -> false + +let exists p a = + try + Array.iter (fun x -> if p x then raise ExitNow) a; + false + with ExitNow -> true + +(** {2 Conversions} *) + +type 'a sequence = ('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_seq a k = iter k a + +let of_seq 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 = Sequence.of_list l in \ + of_seq g |> to_seq |> Sequence.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 print ?(start="[|") ?(stop="|]") ?(sep=";") pp_item out a = + Format.pp_print_string out start; + for k = 0 to Array.length a - 1 do + if k > 0 then ( + Format.pp_print_string out sep; + Format.pp_print_cut out () + ); + pp_item out a.(k) + done; + Format.pp_print_string out stop; + () diff --git a/src/data/CCImmutArray.mli b/src/data/CCImmutArray.mli new file mode 100644 index 00000000..5bb8d910 --- /dev/null +++ b/src/data/CCImmutArray.mli @@ -0,0 +1,85 @@ + +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Immutable Arrays} + + Purely functional use of arrays. Update is costly, but reads are very fast. + Sadly, it is not possible to make this type covariant without using black + magic. + + @since NEXT_RELEASE *) + +type 'a t +(** Array of values of type 'a. The underlying type really is + an array, but it will never be modified. + + It should be covariant but OCaml will not accept it. *) + +val empty : 'a t + +val length : _ t -> int + +val singleton : 'a -> 'a t + +val doubleton : 'a -> 'a -> 'a t + +val make : int -> 'a -> 'a t +(** [make n x] makes an array of [n] times [x] *) + +val init : int -> (int -> 'a) -> 'a t +(** [init n f] makes the array [[| f 0; f 1; ... ; f (n-1) |]]. + @raise Invalid_argument if [n < 0] *) + +val get : 'a t -> int -> 'a +(** Access the element *) + +val set : 'a t -> int -> 'a -> 'a t +(** Copy the array and modify its copy *) + +val map : ('a -> 'b) -> 'a t -> 'b t + +val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t + +val append : 'a t -> 'a t -> 'a t + +val iter : ('a -> unit) -> 'a t -> unit + +val iteri : (int -> 'a -> unit) -> 'a t -> unit + +val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a + +val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a + +val for_all : ('a -> bool) -> 'a t -> bool + +val exists : ('a -> bool) -> 'a t -> bool + +(** {2 Conversions} *) + +type 'a sequence = ('a -> unit) -> unit +type 'a gen = unit -> 'a option + +val of_list : 'a list -> 'a t + +val to_list : 'a t -> 'a list + +val of_array_unsafe : 'a array -> 'a t +(** Take ownership of the given array. Careful, the array must {b NOT} + be modified afterwards! *) + +val to_seq : 'a t -> 'a sequence + +val of_seq : 'a sequence -> 'a t + +val of_gen : 'a gen -> 'a t + +val to_gen : 'a t -> 'a gen + +(** {2 IO} *) + +type 'a printer = Format.formatter -> 'a -> unit + +val print : + ?start:string -> ?stop:string -> ?sep:string -> + 'a printer -> 'a t printer +