From ba90da42ac4521fa727c2edc55b2085c9aa29712 Mon Sep 17 00:00:00 2001 From: Gabriel Scherer Date: Tue, 1 Oct 2019 16:53:14 +0200 Subject: [PATCH] List.concat_map : ('a -> 'b list) -> 'a list -> 'b list (#8760) --- Changes | 3 +++ stdlib/list.ml | 8 ++++++++ stdlib/list.mli | 7 +++++++ stdlib/listLabels.mli | 7 +++++++ testsuite/tests/lib-list/test.ml | 4 ++++ 5 files changed, 29 insertions(+) diff --git a/Changes b/Changes index 0daf3933a..8784bcbc0 100644 --- a/Changes +++ b/Changes @@ -184,6 +184,9 @@ Working version - #8716: Optimize [Array.fill] and [Hashtbl.clear] with a new runtime primitive (Alain Frisch, review by David Allsopp, Stephen Dolan and Damien Doligez) +- #8760: List.concat_map : ('a -> 'b list) -> 'a list -> 'b list + (Gabriel Scherer, review by Daniel Bünzli and Thomas Refis) + - #8530: List.sort: avoid duplicate work by chop (Guillaume Munch-Maccagnoni, review by David Allsopp, Damien Doligez and Gabriel Scherer) diff --git a/stdlib/list.ml b/stdlib/list.ml index 2381e06f7..2b9e545b8 100644 --- a/stdlib/list.ml +++ b/stdlib/list.ml @@ -254,6 +254,14 @@ let filter_map f = in aux [] +let concat_map f l = + let rec aux f acc = function + | [] -> rev acc + | x :: l -> + let xs = f x in + aux f (rev_append xs acc) l + in aux f [] l + let partition p l = let rec part yes no = function | [] -> (rev yes, rev no) diff --git a/stdlib/list.mli b/stdlib/list.mli index e1c04465e..b7b6a89b6 100644 --- a/stdlib/list.mli +++ b/stdlib/list.mli @@ -141,6 +141,13 @@ val filter_map : ('a -> 'b option) -> 'a list -> 'b list @since 4.08.0 *) +val concat_map : ('a -> 'b list) -> 'a list -> 'b list +(** [List.concat_map f l] gives the same result as + {!List.concat}[ (]{!List.map}[ f l)]. Tail-recursive. + + @since 4.10.0 +*) + val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a (** [List.fold_left f a [b1; ...; bn]] is [f (... (f (f a b1) b2) ...) bn]. *) diff --git a/stdlib/listLabels.mli b/stdlib/listLabels.mli index dce1e458e..64fbff872 100644 --- a/stdlib/listLabels.mli +++ b/stdlib/listLabels.mli @@ -143,6 +143,13 @@ val filter_map : f:('a -> 'b option) -> 'a list -> 'b list @since 4.08.0 *) +val concat_map : f:('a -> 'b list) -> 'a list -> 'b list +(** [List.concat_map f l] gives the same result as + {!List.concat}[ (]{!List.map}[ f l)]. Tail-recursive. + + @since 4.10.0 +*) + val fold_left : f:('a -> 'b -> 'a) -> init:'a -> 'b list -> 'a (** [List.fold_left f a [b1; ...; bn]] is [f (... (f (f a b1) b2) ...) bn]. *) diff --git a/testsuite/tests/lib-list/test.ml b/testsuite/tests/lib-list/test.ml index e054529d6..5efdbccfe 100644 --- a/testsuite/tests/lib-list/test.ml +++ b/testsuite/tests/lib-list/test.ml @@ -49,6 +49,10 @@ let () = assert (List.compare_length_with ['1'] 1 = 0); assert (List.compare_length_with ['1'] 2 < 0); assert (List.filter_map string_of_even_opt l = ["0";"2";"4";"6";"8"]); + assert (List.concat_map (fun i -> [i; i+1]) [1; 5] = [1; 2; 5; 6]); + assert ( + let count = ref 0 in + List.concat_map (fun i -> incr count; [i; !count]) [1; 5] = [1; 1; 5; 2]); () ;;