diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 3eb56747..21f2569f 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -265,6 +265,27 @@ let scan_left f acc l = 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 + +(*$= & ~printer:Q.Print.(option int) + (Some 15) (reduce (+) [1; 2; 3; 4; 5]) + (Some 3) (reduce CCInt.min [5; 3; 8; 9]) +*) + +(*$= & ~printer:Q.Print.string + "hello world" (reduce_exn (^) ["hello"; " "; "world"]) +*) + +(*$T + try ignore (reduce_exn (+.) []); false with Invalid_argument _ -> true +*) + (*$= & ~printer:Q.Print.(list int) [0;1;3;6] (scan_left (+) 0 [1;2;3]) [0] (scan_left (+) 0 []) diff --git a/src/core/CCList.mli b/src/core/CCList.mli index 0238520f..54c6cda0 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -81,6 +81,16 @@ val scan_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a list -> 'acc list @since 1.2, but only @since 2.2 with labels *) +val reduce : ('a -> 'a -> 'a) -> 'a list -> 'a option +(** [reduce f (hd::tl)] returns [Some (fold_left f hd tl)]. If [l] is empty, + then [None] is returned. + @since NEXT_RELEASE *) + +val reduce_exn : ('a -> 'a -> 'a) -> 'a list -> 'a +(** [reduce_exn] is the unsafe version of {!reduce}. + @raise Invalid_argument if the given list is empty. + @since NEXT_RELEASE *) + val fold_map2 : ('acc -> 'a -> 'b -> 'acc * 'c) -> 'acc -> 'a list -> 'b list -> 'acc * 'c list (** [fold_map2 f init l1 l2] is to [fold_map] what [List.map2] is to [List.map]. @raise Invalid_argument if the lists do not have the same length. diff --git a/src/core/CCListLabels.mli b/src/core/CCListLabels.mli index c1f261d9..1fa40a1d 100644 --- a/src/core/CCListLabels.mli +++ b/src/core/CCListLabels.mli @@ -85,6 +85,16 @@ val scan_left : f:('acc -> 'a -> 'acc) -> init:'acc -> 'a list -> 'acc list @since 1.2, but only @since 2.2 with labels *) +val reduce : f:('a -> 'a -> 'a) -> 'a list -> 'a option +(** [reduce f (hd::tl)] returns [Some (fold_left f hd tl)]. If [l] is empty, + then [None] is returned. + @since NEXT_RELEASE *) + +val reduce_exn : f:('a -> 'a -> 'a) -> 'a list -> 'a +(** [reduce_exn] is the unsafe version of {!reduce}. + @raise Invalid_argument if the given list is empty. + @since NEXT_RELEASE *) + val fold_map2 : f:('acc -> 'a -> 'b -> 'acc * 'c) -> init:'acc -> 'a list -> 'b list -> 'acc * 'c list (** [fold_map2 ~f ~init l1 l2] is to [fold_map] what [List.map2] is to [List.map]. @raise Invalid_argument if the lists do not have the same length.