File 3721-Add-map-filtermap-functions-to-sets-ordsets-and-gb_s.patch of Package erlang
From be1d1f71a18796f083aaa2a852dd01e653083186 Mon Sep 17 00:00:00 2001
From: Maria Scott <maria-12648430@hnc-agency.org>
Date: Tue, 16 May 2023 14:59:48 +0200
Subject: [PATCH] Add map/filtermap functions to sets, ordsets and gb_sets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Jan Uhlig <juhlig@hnc-agency.org>
Co-authored-by: Björn Gustavsson <bgustavsson@gmail.com>
---
lib/stdlib/doc/src/gb_sets.xml | 18 ++++++++++++++++
lib/stdlib/doc/src/ordsets.xml | 18 ++++++++++++++++
lib/stdlib/doc/src/sets.xml | 22 +++++++++++++++++++
lib/stdlib/src/gb_sets.erl | 34 ++++++++++++++++++++++++++++-
lib/stdlib/src/ordsets.erl | 23 +++++++++++++++++++-
lib/stdlib/src/sets.erl | 36 ++++++++++++++++++++++++++++++-
lib/stdlib/test/sets_SUITE.erl | 35 ++++++++++++++++++++++++++----
lib/stdlib/test/sets_test_lib.erl | 10 +++++++++
8 files changed, 189 insertions(+), 7 deletions(-)
diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml
index c1162f0dba..4107ba0c63 100644
--- a/lib/stdlib/doc/src/gb_sets.xml
+++ b/lib/stdlib/doc/src/gb_sets.xml
@@ -177,6 +177,15 @@
</desc>
</func>
+ <func>
+ <name name="filtermap" arity="2" since="OTP 26.1"/>
+ <fsummary>Filter and map set elements.</fsummary>
+ <desc>
+ <p>Filters and maps elements in <c><anno>Set1</anno></c> using function
+ <c><anno>Fun</anno></c>.</p>
+ </desc>
+ </func>
+
<func>
<name name="fold" arity="3" since=""/>
<fsummary>Fold over set elements.</fsummary>
@@ -335,6 +344,15 @@
</desc>
</func>
+ <func>
+ <name name="map" arity="2" since="OTP 26.1"/>
+ <fsummary>Map set elements.</fsummary>
+ <desc>
+ <p>Maps elements in <c><anno>Set1</anno></c> using mapping function
+ <c><anno>Fun</anno></c>.</p>
+ </desc>
+ </func>
+
<func>
<name name="new" arity="0" since=""/>
<fsummary>Return an empty set.</fsummary>
diff --git a/lib/stdlib/doc/src/ordsets.xml b/lib/stdlib/doc/src/ordsets.xml
index b3efde76b4..0026377f73 100644
--- a/lib/stdlib/doc/src/ordsets.xml
+++ b/lib/stdlib/doc/src/ordsets.xml
@@ -91,6 +91,15 @@
</desc>
</func>
+ <func>
+ <name name="filtermap" arity="2" since="OTP 26.1"/>
+ <fsummary>Filter and map set elements.</fsummary>
+ <desc>
+ <p>Filters and maps elements in <c><anno>Ordset1</anno></c> with function
+ <c><anno>Fun</anno></c>.</p>
+ </desc>
+ </func>
+
<func>
<name name="fold" arity="3" since=""/>
<fsummary>Fold over set elements.</fsummary>
@@ -176,6 +185,15 @@
</desc>
</func>
+ <func>
+ <name name="map" arity="2" since="OTP 26.1"/>
+ <fsummary>Map set elements.</fsummary>
+ <desc>
+ <p>Maps elements in <c><anno>Ordset1</anno></c> with mapping function
+ <c><anno>Fun</anno></c>.</p>
+ </desc>
+ </func>
+
<func>
<name name="new" arity="0" since=""/>
<fsummary>Return an empty set.</fsummary>
diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml
index 4b70fe4add..4465172848 100644
--- a/lib/stdlib/doc/src/sets.xml
+++ b/lib/stdlib/doc/src/sets.xml
@@ -84,6 +84,8 @@
</item>
<item><seemfa marker="#filter/2"><c>filter/2</c></seemfa>
</item>
+ <item><seemfa marker="#filtermap/2"><c>filtermap/2</c></seemfa>
+ </item>
<item><seemfa marker="#fold/3"><c>fold/3</c></seemfa>
</item>
<item><seemfa marker="#from_list/1"><c>from_list/1</c></seemfa>
@@ -100,6 +102,8 @@
</item>
<item><seemfa marker="#is_subset/2"><c>is_subset/2</c></seemfa>
</item>
+ <item><seemfa marker="#map/2"><c>map/2</c></seemfa>
+ </item>
<item><seemfa marker="#new/0"><c>new/0</c></seemfa>
</item>
<item><seemfa marker="#size/1"><c>size/1</c></seemfa>
@@ -173,6 +177,15 @@ true</pre>
</desc>
</func>
+ <func>
+ <name name="filtermap" arity="2" since="OTP 26.1"/>
+ <fsummary>Filter and map set elements.</fsummary>
+ <desc>
+ <p>Filters and maps elements in <c><anno>Set1</anno></c> with function
+ <c><anno>Fun</anno></c>.</p>
+ </desc>
+ </func>
+
<func>
<name name="fold" arity="3" since=""/>
<fsummary>Fold over set elements.</fsummary>
@@ -267,6 +280,15 @@ true</pre>
</desc>
</func>
+ <func>
+ <name name="map" arity="2" since="OTP 26.1"/>
+ <fsummary>Map set elements.</fsummary>
+ <desc>
+ <p>Maps elements in <c><anno>Set1</anno></c> with mapping function
+ <c><anno>Fun</anno></c>.</p>
+ </desc>
+ </func>
+
<func>
<name name="new" arity="0" since=""/>
<fsummary>Return an empty set.</fsummary>
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index aba1bed156..217dff2b3c 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -156,7 +156,8 @@
union/1, intersection/2, intersection/1, is_disjoint/2, difference/2,
is_subset/2, to_list/1, from_list/1, from_ordset/1, smallest/1,
largest/1, take_smallest/1, take_largest/1, iterator/1,
- iterator_from/2, next/1, filter/2, fold/3, is_set/1]).
+ iterator_from/2, next/1, filter/2, fold/3, map/2, filtermap/2,
+ is_set/1]).
%% `sets' compatibility aliases:
@@ -876,6 +877,37 @@ is_set(_) -> false.
filter(F, S) when is_function(F, 1) ->
from_ordset([X || X <- to_list(S), F(X)]).
+-spec map(Fun, Set1) -> Set2 when
+ Fun :: fun((Element1) -> Element2),
+ Set1 :: set(Element1),
+ Set2 :: set(Element2).
+
+map(F, {_, T}) when is_function(F, 1) ->
+ from_list(map_1(T, F, [])).
+
+map_1({Key, Small, Big}, F, L) ->
+ map_1(Small, F, [F(Key) | map_1(Big, F, L)]);
+map_1(nil, _F, L) -> L.
+
+-spec filtermap(Fun, Set1) -> Set2 when
+ Fun :: fun((Element1) -> boolean() | {true, Element2}),
+ Set1 :: set(Element1),
+ Set2 :: set(Element1 | Element2).
+
+filtermap(F, {_, T}) when is_function(F, 1) ->
+ from_list(filtermap_1(T, F, [])).
+
+filtermap_1({Key, Small, Big}, F, L) ->
+ case F(Key) of
+ true ->
+ filtermap_1(Small, F, [Key | filtermap_1(Big, F, L)]);
+ {true,Val} ->
+ filtermap_1(Small, F, [Val | filtermap_1(Big, F, L)]);
+ false ->
+ filtermap_1(Small, F, filtermap_1(Big, F, L))
+ end;
+filtermap_1(nil, _F, L) -> L.
+
-spec fold(Function, Acc0, Set) -> Acc1 when
Function :: fun((Element, AccIn) -> AccOut),
Acc0 :: Acc,
diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl
index 9011c44e5b..bad539c9f4 100644
--- a/lib/stdlib/src/ordsets.erl
+++ b/lib/stdlib/src/ordsets.erl
@@ -24,7 +24,7 @@
-export([union/2,union/1,intersection/2,intersection/1]).
-export([is_disjoint/2]).
-export([subtract/2,is_subset/2]).
--export([fold/3,filter/2]).
+-export([fold/3,filter/2,map/2,filtermap/2]).
-export_type([ordset/1]).
@@ -262,3 +262,24 @@ fold(F, Acc, Set) ->
filter(F, Set) ->
lists:filter(F, Set).
+
+%% map(Fun, OrdSet) -> OrdSet.
+%% Map OrdSet with Fun.
+
+-spec map(Fun, Ordset1) -> Ordset2 when
+ Fun :: fun((Element1 :: T1) -> Element2 :: T2),
+ Ordset1 :: ordset(T1),
+ Ordset2 :: ordset(T2).
+
+map(F, Set) ->
+ from_list(lists:map(F, Set)).
+
+%% filtermap(Fun, OrdSet) -> OrdSet.
+%% Filter and map Ordset with Fun.
+-spec filtermap(Fun, Ordset1) -> Ordset2 when
+ Fun :: fun((Element1 :: T1) -> boolean | ({true, Element2 :: T2})),
+ Ordset1 :: ordset(T1),
+ Ordset2 :: ordset(T1 | T2).
+
+filtermap(F, Set) ->
+ from_list(lists:filtermap(F, Set)).
diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl
index dccc6dcf3a..27e6038f67 100644
--- a/lib/stdlib/src/sets.erl
+++ b/lib/stdlib/src/sets.erl
@@ -46,7 +46,7 @@
-export([union/2,union/1,intersection/2,intersection/1]).
-export([is_disjoint/2]).
-export([subtract/2,is_subset/2]).
--export([fold/3,filter/2]).
+-export([fold/3,filter/2,map/2,filtermap/2]).
-export([new/1, from_list/2]).
-export_type([set/0, set/1]).
@@ -483,6 +483,34 @@ filter_1(Fun, Iter) ->
[]
end.
+%% map(Fun, Set) -> Set.
+%% Map Set with Map.
+-spec map(Fun, Set1) -> Set2 when
+ Fun :: fun((Element1) -> Element2),
+ Set1 :: set(Element1),
+ Set2 :: set(Element2).
+map(F, #set{}=D) when is_function(F, 1) ->
+ fold(fun(E, Acc) -> add_element(F(E), Acc) end,
+ sets:new(),
+ D).
+
+%% filtermap(Fun, Set) -> Set.
+%% Filter and map Set with Fun.
+-spec filtermap(Fun, Set1) -> Set2 when
+ Fun :: fun((Element1) -> boolean() | {true, Element2}),
+ Set1 :: set(Element1),
+ Set2 :: set(Element1 | Element2).
+filtermap(F, #set{}=D) when is_function(F, 1) ->
+ fold(fun(E0, Acc) ->
+ case F(E0) of
+ true -> add_element(E0, Acc);
+ {true, E1} -> add_element(E1, Acc);
+ false -> Acc
+ end
+ end,
+ sets:new(),
+ D).
+
%% get_slot(Hashdb, Key) -> Slot.
%% Get the slot. First hash on the new range, if we hit a bucket
%% which has not been split use the unsplit buddy bucket.
diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl
index c7cb7bad52..ea0b8c32b0 100644
--- a/lib/stdlib/test/sets_SUITE.erl
+++ b/lib/stdlib/test/sets_SUITE.erl
@@ -28,8 +28,8 @@
init_per_testcase/2,end_per_testcase/2,
create/1,add_element/1,del_element/1,
subtract/1,intersection/1,union/1,is_subset/1,
- is_disjoint/1,is_set/1,is_empty/1,fold/1,filter/1,
- take_smallest/1,take_largest/1, iterate/1]).
+ is_disjoint/1,is_set/1,is_empty/1,fold/1,filter/1, map/1,
+ filtermap/1, take_smallest/1,take_largest/1, iterate/1]).
-include_lib("common_test/include/ct.hrl").
@@ -47,8 +47,9 @@ suite() ->
all() ->
[create, add_element, del_element, subtract,
- intersection, union, is_subset, is_set, fold, filter,
- take_smallest, take_largest, iterate, is_empty, is_disjoint].
+ intersection, union, is_subset, is_set, fold, filter, map,
+ filtermap, take_smallest, take_largest, iterate, is_empty,
+ is_disjoint].
groups() ->
[].
@@ -393,6 +394,32 @@ filter_1(List, M) ->
M(filter, {IsNumber,S})}),
M(filter, {fun(X) -> is_atom(X) end,S}).
+map(Config) when is_list(Config) ->
+ test_all([{0,69},{126,130},{254,259},{510,513},{1023,1025},{7999,8000}],
+ fun map_1/2).
+
+map_1(List, M) ->
+ S = M(from_list, List),
+ ToTuple = fun(X) -> {X} end,
+ M(equal, {M(from_list, lists:map(ToTuple, List)),
+ M(map, {ToTuple, S})}),
+ M(map, {fun(_) -> x end, S}).
+
+filtermap(Config) when is_list(Config) ->
+ test_all([{0,69},{126,130},{254,259},{510,513},{1023,1025},{7999,8000}],
+ fun filtermap_1/2).
+
+filtermap_1(List, M) ->
+ S = M(from_list, List),
+ FMFun = fun
+ (X) when is_float(X) -> false;
+ (X) when is_integer(X) -> true;
+ (X) -> {true, {X}}
+ end,
+ M(equal, {M(from_list, lists:filtermap(FMFun, List)),
+ M(filtermap, {FMFun, S})}),
+ M(empty, []).
+
%%%
%%% Test specifics for gb_sets.
%%%
diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl
index 3d2fffbe45..340dd9b1f2 100644
--- a/lib/stdlib/test/sets_test_lib.erl
+++ b/lib/stdlib/test/sets_test_lib.erl
@@ -31,6 +31,7 @@ new(Mod, Eq, New, FromList) ->
(empty, []) -> New();
(equal, {S1,S2}) -> Eq(S1, S2);
(filter, {F,S}) -> filter(Mod, F, S);
+ (filtermap, {F,S}) -> filtermap(Mod, F, S);
(fold, {F,A,S}) -> fold(Mod, F, A, S);
(from_list, L) -> FromList(L);
(intersection, {S1,S2}) -> intersection(Mod, Eq, S1, S2);
@@ -41,6 +42,7 @@ new(Mod, Eq, New, FromList) ->
(is_subset, {S,Set}) -> is_subset(Mod, Eq, S, Set);
(iterator, S) -> Mod:iterator(S);
(iterator_from, {Start, S}) -> Mod:iterator_from(Start, S);
+ (map, {F, S}) -> map(Mod, F, S);
(module, []) -> Mod;
(next, I) -> Mod:next(I);
(singleton, E) -> singleton(Mod, FromList, E);
@@ -121,3 +123,11 @@ fold(Mod, F, A, S) ->
filter(Mod, F, S) ->
true = Mod:is_set(S),
Mod:filter(F, S).
+
+map(Mod, F, S) ->
+ true = Mod:is_set(S),
+ Mod:map(F, S).
+
+filtermap(Mod, F, S) ->
+ true = Mod:is_set(S),
+ Mod:filtermap(F, S).
--
2.35.3