From 2a872907a11bdd58f412dcc6ae17bcaeff3ba021 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 19 Oct 2016 18:17:40 +0200 Subject: [PATCH] add `CCArray.{sorted,sort_indices,sort_ranking}` (closes #81) --- src/core/CCArray.ml | 139 +++++++++++++++++++++++++++++++++++++++++++ src/core/CCArray.mli | 23 +++++++ 2 files changed, 162 insertions(+) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index cc7b622f..f83b4360 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -51,6 +51,29 @@ module type S = sig val reverse_in_place : 'a t -> unit (** Reverse the array in place *) + val sorted : ('a -> 'a -> int) -> 'a t -> 'a array + (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. + @since NEXT_RELEASE *) + + 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 of the [i]-th element in [sort cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. + [a] is not modified. + @since NEXT_RELEASE *) + + 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], + such that [b.(i)] is the position in [sorted cmp a] of the [i]-th + element of [a]. + [a] is not modified. + + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + + Without duplicates, we also have + [lookup_exn (sorted a) a.(i) = (sorted_ranking a).(i)] + @since NEXT_RELEASE *) + val find : ('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such that [f x = Some y], else it returns [None] *) @@ -260,6 +283,18 @@ let _shuffle _rand_int a i j = let b = Array.copy a in shuffle_with st a; a <> b *) +let _sort_indices cmp a i j = + let len = j-i in + let b = Array.init len (fun k->k) in + Array.sort (fun k1 k2 -> cmp a.(k1+i) a.(k2+i)) b; + b + +let _sorted cmp a i j = + let len = j-i in + let b = Array.sub a i len in + Array.sort cmp b; + b + let _choose a i j st = if i>=j then raise Not_found; a.(i+Random.State.int st (j-i)) @@ -366,6 +401,48 @@ let reverse_in_place a = a = [| 6;5;4;3;2;1 |] *) +let sorted cmp a = _sorted cmp a 0 (Array.length a) + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] (sorted Pervasives.compare [||]) + [|0;1;2;3;4|] (sorted Pervasives.compare [|3;2;1;4;0|]) + *) + +(*$Q + Q.(array int) (fun a -> \ + let b = Array.copy a in \ + Array.sort Pervasives.compare b; b = sorted Pervasives.compare a) +*) + +let sort_indices cmp a = _sort_indices cmp a 0 (Array.length a) + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] (sort_indices Pervasives.compare [||]) + [|4;2;1;0;3|] (sort_indices Pervasives.compare [|"d";"c";"b";"e";"a"|]) +*) + +(*$Q + Q.(array printable_string) (fun a -> \ + let b = sort_indices String.compare a in \ + sorted String.compare a = Array.map (Array.get a) b) +*) + +let sort_ranking cmp a = + let cmp_int : int -> int -> int = Pervasives.compare in + sort_indices cmp_int (sort_indices cmp a) + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] (sort_ranking Pervasives.compare [||]) + [|3;2;1;4;0|] (sort_ranking Pervasives.compare [|"d";"c";"b";"e";"a"|]) +*) + +(*$Q + Q.(array printable_string) (fun a -> \ + let b = sort_ranking String.compare a in \ + let a_sorted = sorted String.compare a in \ + a = Array.map (Array.get a_sorted) b) +*) + let rev a = let b = Array.copy a in reverse_in_place b; @@ -689,6 +766,68 @@ module Sub = struct Sub.reverse_in_place s; a = [| 1; 2; 5; 4; 3; 6 |] *) + let sorted cmp a = _sorted cmp a.arr a.i a.j + + (*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ + Sub.sorted Pervasives.compare s) + [|2;3;4|] \ + (let a = [|6;5;4;3;2;1|] in let s = Sub.make a 2 ~len:3 in \ + Sub.sorted Pervasives.compare s) + *) + + (*$Q + Q.(array int) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = Sub.make a 5 ~len:5 in \ + let b = Array.sub a 5 5 in \ + Array.sort Pervasives.compare b; b = Sub.sorted Pervasives.compare s)) + *) + + let sort_ranking cmp a = + let idx = _sort_indices cmp a.arr a.i a.j in + let cmp_int : int -> int -> int = Pervasives.compare in + sort_indices cmp_int idx + + (*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ + Sub.sort_ranking Pervasives.compare s) + [|2;1;3;0|] \ + (let a = [|"d";"c";"b";"e";"a"|] in let s = Sub.make a 1 ~len:4 in \ + Sub.sort_ranking Pervasives.compare s) + *) + + (*$Q + Q.(array printable_string) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = Sub.make a 5 ~len:5 in \ + let b = Sub.sort_indices String.compare s in \ + Sub.sorted String.compare s = Array.map (Sub.get s) b)) + *) + + let sort_indices cmp a = _sort_indices cmp a.arr a.i a.j + + (*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ + Sub.sort_indices Pervasives.compare s) + [|3;1;0;2|] \ + (let a = [|"d";"c";"b";"e";"a"|] in let s = Sub.make a 1 ~len:4 in \ + Sub.sort_indices Pervasives.compare s) + *) + + (*$Q + Q.(array printable_string) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = Sub.make a 5 ~len:5 in \ + let b = Sub.sort_ranking String.compare s in \ + let a_sorted = Sub.sorted String.compare s in \ + Sub.copy s = Array.map (Array.get a_sorted) b)) + *) + + let find f a = _find (fun _ -> f) a.arr a.i a.j let findi f a = _find (fun i -> f (i-a.i)) a.arr a.i a.j diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 8206d2cb..c22045f4 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -53,6 +53,29 @@ module type S = sig val reverse_in_place : 'a t -> unit (** Reverse the array in place *) + val sorted : ('a -> 'a -> int) -> 'a t -> 'a array + (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. + @since NEXT_RELEASE *) + + 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 of the [i]-th element in [sort cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. + [a] is not modified. + @since NEXT_RELEASE *) + + 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], + such that [b.(i)] is the position in [sorted cmp a] of the [i]-th + element of [a]. + [a] is not modified. + + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + + Without duplicates, we also have + [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] + @since NEXT_RELEASE *) + val find : ('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such that [f x = Some y], else it returns [None] *)