From 8d532f9a00283a95781292b1bd9c411f7b5ee791 Mon Sep 17 00:00:00 2001 From: favonia Date: Mon, 24 May 2021 09:23:11 -0500 Subject: [PATCH] feat(list): add sorted_diff_uniq Also fixed documentation --- src/core/CCList.ml | 34 ++++++++++++++++++++++++++++++++++ src/core/CCList.mli | 22 ++++++++++++++++++---- src/core/CCListLabels.mli | 22 ++++++++++++++++++---- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 5799d71e..538cab4d 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -958,6 +958,40 @@ let sorted_merge_uniq ~cmp l1 l2 = uniq_succ ~eq:CCInt.equal l3 = l3) *) +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 + +(*$T + sorted_diff_uniq ~cmp:CCInt.compare [1; 1; 1; 2; 2; 3; 5; 8; 8; 8] [1; 2; 2; 2; 2; 8; 13; 13; 13] = [1;3;5;8] +*) + +(*$Q + Q.(pair (list small_int) (list small_int)) (fun (l1, l2) -> \ + let l1 = List.sort CCInt.compare l1 in \ + let l2 = List.sort CCInt.compare l2 in \ + is_sorted ~cmp:CCInt.compare (sorted_diff_uniq ~cmp:CCInt.compare l1 l2)) + Q.(pair (list small_int) (list small_int)) (fun (l1, l2) -> \ + let l1 = List.sort CCInt.compare l1 in \ + let l2 = List.sort CCInt.compare l2 in \ + sorted_diff_uniq ~cmp:CCInt.compare l1 l2 = uniq_succ ~eq:CCInt.equal (sorted_diff ~cmp:CCInt.compare l1 l2)) +*) + let take n l = let rec direct i n l = match l with | [] -> [] diff --git a/src/core/CCList.mli b/src/core/CCList.mli index 28c39213..fcf70f61 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -504,8 +504,10 @@ val sorted_merge : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list val sorted_diff : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list (** [sorted_diff ~cmp l1 l2] returns the elements in [l1] that are not in [l2]. Both lists are assumed to be sorted with respect to [cmp] and - duplicate elements are treated individually. It is the left inverse of - [sorted_merge]; that is, [sorted_diff ~cmp (sorted_merge ~cmp l1 l2) l2] + duplicate elements in the input lists are treated individually; + for example, [sorted_diff ~cmp [1;1;1;2;2;3] [1;2;2]] would be [[1;1;3]]. + It is the left inverse of [sorted_merge]; that is, + [sorted_diff ~cmp (sorted_merge ~cmp l1 l2) l2] is always equal to [l1] for sorted lists [l1] and [l2]. @since NEXT_RELEASE *) @@ -518,6 +520,16 @@ val sorted_merge_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list removes duplicates. @since 0.10 *) +val sorted_diff_uniq : cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list +(** [sorted_diff_uniq ~cmp l1 l2] collects the elements in [l1] that are not in [l2] + and then remove duplicates. + Both lists are assumed to be sorted with respect to [cmp] and + duplicate elements in the input lists are treated individually; + for example, [sorted_diff_uniq ~cmp [1;1;1;2;2] [1;2;2;2]] would be [[1]]. + [sorted_diff_uniq ~cmp l1 l2] and [uniq_succ ~eq (sorted_diff ~cmp l1 l2)] + always give the same result for sorted [l1] and [l2] and compatible [cmp] and [eq]. + @since NEXT_RELEASE *) + val is_sorted : cmp:('a -> 'a -> int) -> 'a list -> bool (** [is_sorted ~cmp l] returns [true] iff [l] is sorted (according to given order). @param cmp the comparison function. @@ -535,8 +547,10 @@ val sorted_insert : cmp:('a -> 'a -> int) -> ?uniq:bool -> 'a -> 'a list -> 'a l @since 0.17 *) val sorted_remove : cmp:('a -> 'a -> int) -> ?all:bool -> 'a -> 'a list -> 'a list -(** [sorted_insert ~cmp ~key l] removes [key] from a sorted list [l] such that - the return value is sorted too. +(** [sorted_remove ~cmp x l] removes [x] from a sorted list [l] such that + the return value is sorted too. By default, it is the left inverse of + [sorted_insert]; that is, [sorted_remove ~cmp x (sorted_insert ~cmp x l)] + is equal to [l] for any sorted list [l]. @param all if true then all occurrences of [x] will be removed. Otherwise, only the first [x] will be removed (if any). Default [false] (only the first will be removed). @since NEXT_RELEASE *) diff --git a/src/core/CCListLabels.mli b/src/core/CCListLabels.mli index 5085b320..c0b8016a 100644 --- a/src/core/CCListLabels.mli +++ b/src/core/CCListLabels.mli @@ -507,8 +507,10 @@ val sorted_merge : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a list - val sorted_diff : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a list -> 'a list (** [sorted_diff ~cmp l1 l2] returns the elements in [l1] that are not in [l2]. Both lists are assumed to be sorted with respect to [cmp] and - duplicate elements are treated individually. It is the left inverse of - [sorted_merge]; that is, [sorted_diff ~cmp (sorted_merge ~cmp l1 l2) l2] + duplicate elements in the input lists are treated individually; + for example, [sorted_diff ~cmp [1;1;1;2;2;3] [1;2;2]] would be [[1;1;3]]. + It is the left inverse of [sorted_merge]; that is, + [sorted_diff ~cmp (sorted_merge ~cmp l1 l2) l2] is always equal to [l1] for sorted lists [l1] and [l2]. @since NEXT_RELEASE *) @@ -521,6 +523,16 @@ val sorted_merge_uniq : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a l removes duplicates. @since 0.10 *) +val sorted_diff_uniq : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> 'a list -> 'a list +(** [sorted_diff_uniq ~cmp l1 l2] collects the elements in [l1] that are not in [l2] + and then remove duplicates. + Both lists are assumed to be sorted with respect to [cmp] and + duplicate elements in the input lists are treated individually; + for example, [sorted_diff_uniq ~cmp [1;1;1;2;2] [1;2;2;2]] would be [[1]]. + [sorted_diff_uniq ~cmp l1 l2] and [uniq_succ ~eq (sorted_diff ~cmp l1 l2)] + always give the same result for sorted [l1] and [l2] and compatible [cmp] and [eq]. + @since NEXT_RELEASE *) + val is_sorted : cmp:(('a -> 'a -> int) [@keep_label]) -> 'a list -> bool (** [is_sorted ~cmp l] returns [true] iff [l] is sorted (according to given order). @param cmp the comparison function. @@ -538,8 +550,10 @@ val sorted_insert : cmp:(('a -> 'a -> int) [@keep_label]) -> ?uniq:bool -> 'a -> @since 0.17 *) val sorted_remove : cmp:(('a -> 'a -> int) [@keep_label]) -> ?all:bool -> 'a -> 'a list -> 'a list -(** [sorted_insert ~cmp x l] removes [x] from a sorted list [l] such that - the return value is sorted too. +(** [sorted_remove ~cmp x l] removes [x] from a sorted list [l] such that + the return value is sorted too. By default, it is the left inverse of + [sorted_insert]; that is, [sorted_remove ~cmp x (sorted_insert ~cmp x l)] + is equal to [l] for any sorted list [l]. @param all if true then all occurrences of [x] will be removed. Otherwise, only the first [x] will be removed (if any). Default [false] (only the first will be removed). @since NEXT_RELEASE *)