diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index a66c56cfcf4..1d1ddadf529 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -262,20 +262,49 @@ in the Standard Library. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --doc "Returns a new empty set.". +-doc """ +Returns a new empty set. + +## Examples + +```erlang +> gb_sets:to_list(gb_sets:empty()). +[] +``` +""". -spec empty() -> Set when Set :: set(none()). empty() -> {0, nil}. --doc "Returns a new empty set.". +-doc """ +Returns a new empty set. + +## Examples + +```erlang +> gb_sets:to_list(gb_sets:new()). +[] +``` +""". -spec new() -> Set when Set :: set(none()). new() -> empty(). --doc "Returns `true` if `Set` is an empty set, otherwise `false`.". +-doc """ +Returns `true` if `Set` is an empty set; otherwise, returns `false`. + +## Examples + +```erlang +> gb_sets:is_empty(gb_sets:new()). +true +> gb_sets:is_empty(gb_sets:singleton(1)). +false +``` +""". -spec is_empty(Set) -> boolean() when Set :: set(). @@ -284,7 +313,18 @@ is_empty({0, nil}) -> is_empty(_) -> false. --doc "Returns the number of elements in `Set`.". +-doc """ +Returns the number of elements in `Set`. + +## Examples + +```erlang +> gb_sets:size(gb_sets:new()). +0 +> gb_sets:size(gb_sets:from_list([4,5,6])). +3 +``` +""". -spec size(Set) -> non_neg_integer() when Set :: set(). @@ -292,8 +332,19 @@ size({Size, _}) -> Size. -doc """ -Returns `true` if `Set1` and `Set2` are equal, that is when every element of one -set is also a member of the respective other set, otherwise `false`. +Returns `true` if `Set1` and `Set2` are equal, that is, if every element +of one set is also a member of the other set; otherwise, returns `false`. + +## Examples + +```erlang +> Empty = gb_sets:new(). +> S = gb_sets:from_list([a,b]). +> gb_sets:is_equal(S, S) +true +> gb_sets:is_equal(S, Empty) +false +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec is_equal(Set1, Set2) -> boolean() when @@ -322,7 +373,17 @@ is_equal_1({Key1, Smaller, Bigger}, Keys0) -> throw(not_equal) end. --doc "Returns a set containing only element `Element`.". +-doc """ +Returns a set containing only element `Element`. + +## Examples + +```erlang +> S = gb_sets:singleton(42). +> gb_sets:to_list(S). +[42] +``` +""". -spec singleton(Element) -> set(Element). singleton(Key) -> @@ -335,7 +396,20 @@ singleton(Key) -> is_element(Key, S) -> is_member(Key, S). --doc "Returns `true` if `Element` is an member of `Set`, otherwise `false`.". +-doc """ +Returns `true` if `Element` is an element of `Set`; otherwise, returns +`false`. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b,c]). +> gb_sets:is_member(42, S). +false +> gb_sets:is_member(b, S). +true +``` +""". -spec is_member(Element, Set) -> boolean() when Set :: set(Element). @@ -352,8 +426,20 @@ is_member_1(_, nil) -> false. -doc """ -Returns a new set formed from `Set1` with `Element` inserted. Assumes that -`Element` is not present in `Set1`. +Returns a new set formed from `Set1` with `Element` inserted, +assuming `Element` is not already present. + +## Examples + +```erlang +> S0 = gb_sets:new(). +> S1 = gb_sets:insert(7, S0). +> gb_sets:to_list(S1). +[7] +> S2 = gb_sets:insert(42, S1). +> lists:sort(gb_sets:to_list(S2)). +[7,42] +``` """. -spec insert(Element, Set1) -> Set2 when Set1 :: set(Element), @@ -363,7 +449,7 @@ insert(Key, {S, T}) when is_integer(S), S >= 0 -> S1 = S + 1, {S1, insert_1(Key, T, ?pow(S1, ?p))}. -insert_1(Key, {Key1, Smaller, Bigger}, S) when Key < Key1 -> +insert_1(Key, {Key1, Smaller, Bigger}, S) when Key < Key1 -> case insert_1(Key, Smaller, ?div2(S)) of {T1, H1, S1} when is_integer(H1), is_integer(S1) -> T = {Key1, T1, Bigger}, @@ -380,7 +466,7 @@ insert_1(Key, {Key1, Smaller, Bigger}, S) when Key < Key1 -> T1 -> {Key1, T1, Bigger} end; -insert_1(Key, {Key1, Smaller, Bigger}, S) when Key > Key1 -> +insert_1(Key, {Key1, Smaller, Bigger}, S) when Key > Key1 -> case insert_1(Key, Bigger, ?div2(S)) of {T1, H1, S1} when is_integer(H1), is_integer(S1) -> T = {Key1, Smaller, T1}, @@ -416,10 +502,21 @@ count(nil) -> -doc """ Rebalances the tree representation of `Set1`. -Notice that this is rarely necessary, but can be motivated when a large number of -elements have been deleted from the tree without further insertions. Rebalancing - can then be forced to minimise lookup times, as deletion does not rebalance the -tree. +This is rarely necessary, but can be motivated when a large number of +elements have been deleted from the tree without further +insertions. Forcing rebalancing can minimize lookup times, as deletion +does not rebalance the tree. + +## Examples + +```erlang +> S0 = gb_sets:from_ordset(lists:seq(1, 100)). +> Delete = fun(E, Set) -> gb_sets:delete(E, Set) end. +> S1 = lists:foldl(Delete, S0, lists:seq(1, 50)). +> gb_sets:size(S1). +50 +> S2 = gb_sets:balance(S1). +``` """. -spec balance(Set1) -> Set2 when Set1 :: set(Element), @@ -449,8 +546,23 @@ balance_list_1(L, 0) -> {nil, L}. -doc """ -Returns a new set formed from `Set1` with `Element` inserted. If `Element` is -already an element in `Set1`, nothing is changed. +Returns a new set formed from `Set1` with `Element` inserted. + +If `Element` is already an element in `Set1`, nothing is changed. + +## Examples + +```erlang +> S0 = gb_sets:new(). +> S1 = gb_sets:add_element(7, S0). +> gb_sets:to_list(S1). +[7] +> S2 = gb_sets:add_element(42, S1). +> S2 = gb_sets:add_element(42, S1). +> S2 = gb_sets:add_element(42, S1). +> lists:sort(gb_sets:to_list(S2)). +[7,42] +``` """. -spec add_element(Element, Set1) -> Set2 when Set1 :: set(Element), @@ -475,6 +587,14 @@ add(X, S) -> -doc """ Returns a set of the elements in `List`, where `List` can be unordered and contain duplicates. + +## Examples + +```erlang +> Unordered = [x,y,a,x,y,b,b,z] +> gb_sets:to_list(gb_sets:from_list(Unordered)). +[a,b,x,y,z] +``` """. -spec from_list(List) -> Set when List :: [Element], @@ -484,8 +604,17 @@ from_list(L) -> from_ordset(ordsets:from_list(L)). -doc """ -Turns an ordered-set list `List` into a set. The list must not contain -duplicates. +Turns an ordered-set list `List` into a set. + +The list must not contain duplicates. + +## Examples + +```erlang +> Ordset = [1,2,3]. +> gb_sets:to_list(gb_sets:from_ordset(Ordset)). +[1,2,3] +``` """. -spec from_ordset(List) -> Set when List :: [Element], @@ -504,8 +633,20 @@ del_element(Key, S) -> delete_any(Key, S). -doc """ -Returns a new set formed from `Set1` with `Element` removed. If `Element` is not -an element in `Set1`, nothing is changed. +Returns a new set formed from `Set1` with `Element` removed. + +If `Element` is not an element in `Set1`, nothing is changed. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b]). +> gb_sets:to_list(gb_sets:delete_any(b, S)). +[a] +> S = gb_sets:delete_any(x, S). +> lists:sort(gb_sets:to_list(S)). +[a,b] +``` """. -spec delete_any(Element, Set1) -> Set2 when Set1 :: set(Element), @@ -520,8 +661,16 @@ delete_any(Key, S) -> end. -doc """ -Returns a new set formed from `Set1` with `Element` removed. Assumes that +Returns a new set formed from `Set1` with `Element` removed, assuming `Element` is present in `Set1`. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b]). +> gb_sets:to_list(gb_sets:delete(b, S)). +[a] +``` """. -spec delete(Element, Set1) -> Set2 when Set1 :: set(Element), @@ -548,8 +697,21 @@ merge(Smaller, Larger) -> {Key, Smaller, Larger1}. -doc """ -Returns `{Element, Set2}`, where `Element` is the smallest element in `Set1`, -and `Set2` is this set with `Element` deleted. Assumes that `Set1` is not empty. +Returns `{Element, Set2}`, where `Element` is the smallest element in +`Set1`, and `Set2` is this set with `Element` deleted. + +Assumes that `Set1` is not empty. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c]). +> {Smallest,S1} = gb_sets:take_smallest(S0). +> Smallest. +a +> gb_sets:to_list(S1). +[b,c] +``` """. -spec take_smallest(Set1) -> {Element, Set2} when Set1 :: set(Element), @@ -565,7 +727,19 @@ take_smallest1({Key, Smaller, Larger}) -> {Key1, Smaller1} = take_smallest1(Smaller), {Key1, {Key, Smaller1, Larger}}. --doc "Returns the smallest element in `Set`. Assumes that `Set` is not empty.". +-doc """ +Returns the smallest element in `Set`. + +Assumes that `Set` is not empty. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b,c]). +> gb_sets:smallest(S). +a +``` +""". -spec smallest(Set) -> Element when Set :: set(Element). @@ -578,8 +752,21 @@ smallest_1({_Key, Smaller, _Larger}) -> smallest_1(Smaller). -doc """ -Returns `{Element, Set2}`, where `Element` is the largest element in `Set1`, and -`Set2` is this set with `Element` deleted. Assumes that `Set1` is not empty. +Returns `{Element, Set2}`, where `Element` is the largest element in +`Set1`, and `Set2` is this set with `Element` deleted. + +Assumes that `Set1` is not empty. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c]). +> {Largest,S1} = gb_sets:take_largest(S0). +> Largest. +c +> gb_sets:to_list(S1). +[a,b] +``` """. -spec take_largest(Set1) -> {Element, Set2} when Set1 :: set(Element), @@ -595,7 +782,19 @@ take_largest1({Key, Smaller, Larger}) -> {Key1, Larger1} = take_largest1(Larger), {Key1, {Key, Smaller, Larger1}}. --doc "Returns the largest element in `Set`. Assumes that `Set` is not empty.". +-doc """ +Returns the largest element in `Set`. + +Assumes that `Set` is not empty. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b,c]). +> gb_sets:largest(S). +c +``` +""". -spec largest(Set) -> Element when Set :: set(Element). @@ -612,6 +811,16 @@ Returns `{found, Element2}`, where `Element2` is the greatest element strictly less than `Element1`. Returns `none` if no such element exists. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b,c]). +> gb_sets:smaller(b, S). +{found,a} +> gb_sets:smaller(a, S). +none +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec smaller(Element1, Set) -> none | {found, Element2} when @@ -638,6 +847,16 @@ Returns `{found, Element2}`, where `Element2` is the least element strictly greater than `Element1`. Returns `none` if no such element exists. + +## Examples + +```erlang +> S = gb_sets:from_list([a,b,c]). +> gb_sets:larger(a, S). +{found,b} +> gb_sets:larger(c, S). +none +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec larger(Element1, Set) -> none | {found, Element2} when @@ -659,7 +878,14 @@ larger_1(Key, {Key1, Smaller, _Larger}) when Key < Key1 -> larger_1(Key, {_Key, _Smaller, Larger}) -> larger_1(Key, Larger). --doc "Returns the elements of `Set` as a list.". +-doc """ +Returns the elements of `Set` as an ordered list. + +```erlang +> gb_sets:to_list(gb_sets:from_list([4,3,5,1,2])). +[1,2,3,4,5] +``` +""". -spec to_list(Set) -> List when Set :: set(Element), List :: [Element]. @@ -690,11 +916,21 @@ iterator(Set) -> Returns an iterator that can be used for traversing the entries of `Set` in either `ordered` or `reversed` direction; see `next/1`. -The implementation of this is very efficient; traversing the whole set using -[`next/1`](`next/1`) is only slightly slower than getting the list of all - elements using `to_list/1` and traversing that. The main advantage of the -iterator approach is that it does not require the complete list of all elements -to be built in memory at one time. +The implementation is very efficient; traversing the whole set using +[`next/1`](`next/1`) is only slightly slower than getting the list of +all elements using `to_list/1` and traversing that. The main advantage +of the iterator approach is that it avoids building the complete list +of all elements to be built in memory at once. + +```erlang +> S = gb_sets:from_ordset([1,2,3,4,5]). +> Iter0 = gb_sets:iterator(S, ordered). +> element(1, gb_sets:next(Iter0)). +1 +> Iter1 = gb_sets:iterator(S, reversed). +> element(1, gb_sets:next(Iter1)). +5 +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec iterator(Set, Order) -> Iter when @@ -726,11 +962,22 @@ iterator_r(nil, As) -> -doc """ Returns an iterator that can be used for traversing the entries of `Set`; see -`next/1`. The difference as compared to the iterator returned by `iterator/1` is -that the iterator starts with the first element greater than or equal to +`next/1`. + +Unlike the iterator returned by `iterator/1` or `iterator/2`, this +iterator starts with the first element greater than or equal to `Element`. Equivalent to [`iterator_from(Element, Set, ordered)`](`iterator_from/3`). + +## Examples + +```erlang +> S = gb_sets:from_ordset([10,20,30,40,50]). +> Iter = gb_sets:iterator_from(17, S). +> element(1, gb_sets:next(Iter)). +20 +``` """. -doc(#{since => <<"OTP 18.0">>}). -spec iterator_from(Element, Set) -> Iter when @@ -742,8 +989,20 @@ iterator_from(Element, Set) -> -doc """ Returns an iterator that can be used for traversing the entries of `Set`; see -`next/1`. The difference as compared to the iterator returned by `iterator/2` is -that the iterator starts with the first element next to or equal to `Element`. +`next/1`. + +Unlike the iterator returned by `iterator/1` or `iterator/2`, this +iterator starts with the first element greater than or equal to +`Element`. + +## Examples + +```erlang +> S = gb_sets:from_ordset([10,20,30,40,50]). +> Iter = gb_sets:iterator_from(17, S, reversed). +> element(1, gb_sets:next(Iter)). +10 +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec iterator_from(Element, Set, Order) -> Iter when @@ -778,6 +1037,17 @@ iterator_from_r(_, nil, As) -> Returns `{Element, Iter2}`, where `Element` is the smallest element referred to by iterator `Iter1`, and `Iter2` is the new iterator to be used for traversing the remaining elements, or the atom `none` if no elements remain. + +```erlang +> S = gb_sets:from_ordset([1,2,3,4,5]). +> Iter0 = gb_sets:iterator(S). +> {Element0, Iter1} = gb_sets:next(Iter0). +> Element0. +1 +> {Element1, Iter2} = gb_sets:next(Iter1). +> Element1. +2 +``` """. -spec next(Iter1) -> {Element, Iter2} | 'none' when Iter1 :: iter(Element), @@ -813,7 +1083,22 @@ next({_, []}) -> %% traversing the elements can be devised, but they all have higher %% overhead. --doc "Returns the merged (union) set of `Set1` and `Set2`.". +-doc """ +Returns the union of `Set1` and `Set2`. + +The union of two sets is a new set that contains all the elements from +both sets, without duplicates. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c,d]). +> S1 = gb_sets:from_list([c,d,e,f]). +> Union = gb_sets:union(S0, S1). +> gb_sets:to_list(Union). +[a,b,c,d,e,f] +``` +""". -spec union(Set1, Set2) -> Set3 when Set1 :: set(Element), Set2 :: set(Element), @@ -919,7 +1204,21 @@ balance_revlist_1([Key | L], 1) -> balance_revlist_1(L, 0) -> {nil, L}. --doc "Returns the merged (union) set of the list of sets.". +-doc """ +Returns the union of a list of sets. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c,d]). +> S1 = gb_sets:from_list([d,e,f]). +> S2 = gb_sets:from_list([q,r]) +> Sets = [S0, S1, S2]. +> Union = gb_sets:union(Sets). +> gb_sets:to_list(Union). +[a,b,c,d,e,f,q,r] +``` +""". -spec union(SetList) -> Set when SetList :: [set(Element),...], Set :: set(Element). @@ -935,7 +1234,24 @@ union_list(S, []) -> S. %% The rest is modelled on the above. --doc "Returns the intersection of `Set1` and `Set2`.". +-doc """ +Returns the intersection of `Set1` and `Set2`. + +The intersection of two sets is a new set that contains only the +elements that are present in both sets. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c,d]). +> S1 = gb_sets:from_list([c,d,e,f]). +> S2 = gb_sets:from_list([q,r]). +> gb_sets:to_list(gb_sets:intersection(S0, S1)). +[c,d] +> gb_sets:to_list(gb_sets:intersection(S1, S2)). +[] +``` +""". -spec intersection(Set1, Set2) -> Set3 when Set1 :: set(Element), Set2 :: set(Element), @@ -987,7 +1303,25 @@ intersection_2([], _, As, S) -> intersection_2(_, [], As, S) -> {S, balance_revlist(As, S)}. --doc "Returns the intersection of the non-empty list of sets.". +-doc """ +Returns the intersection of the non-empty list of sets. + +The intersection of two sets is a new set that contains only the +elements that are present in both sets. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c,d]). +> S1 = gb_sets:from_list([d,e,f]). +> S2 = gb_sets:from_list([q,r]) +> Sets = [S0, S1, S2]. +> gb_sets:to_list(gb_sets:intersection([S0, S1, S2])). +[] +> gb_sets:to_list(gb_sets:intersection([S0, S1])). +[d] +``` +""". -spec intersection(SetList) -> Set when SetList :: [set(Element),...], Set :: set(Element). @@ -1000,8 +1334,25 @@ intersection_list(S, [S1 | Ss]) -> intersection_list(S, []) -> S. -doc """ -Returns `true` if `Set1` and `Set2` are disjoint (have no elements in common), -otherwise `false`. +Returns `true` if `Set1` and `Set2` are disjoint; otherwise, returns +`false`. + +Two sets are disjoint if they have no elements in common. + +This function is equivalent to `gb_sets:intersection(Set1, Set2) =:= []`, +but faster. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c,d]). +> S1 = gb_sets:from_list([d,e,f]). +> S2 = gb_sets:from_list([q,r]) +> gb_sets:is_disjoint(S0, S1). +false +> gb_sets:is_disjoint(S1, S2). +true +``` """. -spec is_disjoint(Set1, Set2) -> boolean() when Set1 :: set(Element), @@ -1096,8 +1447,21 @@ difference_2(Xs, [], As, S) -> %% without the construction of a new set. -doc """ -Returns `true` when every element of `Set1` is also a member of `Set2`, -otherwise `false`. +Returns `true` when every element of `Set1` is also a member of `Set2`; +otherwise, returns `false`. + +## Examples + +```erlang +> S0 = gb_sets:from_list([a,b,c,d]). +> S1 = gb_sets:from_list([c,d]). +> gb_sets:is_subset(S1, S0). +true +> gb_sets:is_subset(S0, S1). +false +> gb_sets:is_subset(S0, S0). +true +``` """. -spec is_subset(Set1, Set2) -> boolean() when Set1 :: set(Element), @@ -1144,10 +1508,28 @@ is_subset_2(_, []) -> %% For compatibility with `sets': -doc """ -Returns `true` if `Term` appears to be a set, otherwise `false`. This function -will return `true` for any term that coincides with the representation of a -`gb_set`, while not really being a `gb_set`, thus it might return false positive -results. See also note on [data types](`e:system:data_types.md#no_user_types`). +Returns `true` if `Term` appears to be a set; otherwise, returns `false`. + +> #### Note {: .info } +> +> This function will return `true` for any term that coincides with the +> representation of a `gb_set`, while not really being a `gb_set`, thus +> it might return false positive results. See also note on [data +> types](`e:system:data_types.md#no_user_types`). +> +> Furthermore, since gb_sets are opaque, calling this function on terms +> that are not gb_sets could result in `m:dialyzer` warnings. + +## Examples + +```erlang +> gb_sets:is_set(gb_sets:new()). +true +> gb_sets:is_set(gb_sets:singleton(42)). +true +> gb_sets:is_set(0). +false +``` """. -spec is_set(Term) -> boolean() when Term :: term(). @@ -1156,7 +1538,19 @@ is_set({0, nil}) -> true; is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true; is_set(_) -> false. --doc "Filters elements in `Set1` using predicate function `Pred`.". +-doc """ +Filters elements in `Set1` using predicate function `Pred`. + +## Examples + +```erlang +> S = gb_sets:from_list([1,2,3,4,5,6,7]). +> IsEven = fun(N) -> N rem 2 =:= 0 end. +> Filtered = gb_sets:filter(IsEven, S). +> gb_sets:to_list(Filtered). +[2,4,6] +``` +""". -spec filter(Pred, Set1) -> Set2 when Pred :: fun((Element) -> boolean()), Set1 :: set(Element), @@ -1165,7 +1559,19 @@ is_set(_) -> false. filter(F, S) when is_function(F, 1) -> from_ordset([X || X <- to_list(S), F(X)]). --doc "Maps elements in `Set1` using mapping function `Fun`.". +-doc """ +Maps elements in `Set1` with mapping function `Fun`. + +## Examples + +```erlang +> S = gb_sets:from_list([1,2,3,4,5,6,7]). +> F = fun(N) -> N div 2 end. +> Mapped = gb_sets:map(F, S). +> gb_sets:to_list(Mapped). +[0,1,2,3] +``` +""". -doc(#{since => <<"OTP 27.0">>}). -spec map(Fun, Set1) -> Set2 when Fun :: fun((Element1) -> Element2), @@ -1179,7 +1585,36 @@ map_1({Key, Small, Big}, F, L) -> map_1(Small, F, [F(Key) | map_1(Big, F, L)]); map_1(nil, _F, L) -> L. --doc "Filters and maps elements in `Set1` using function `Fun`.". +-doc """ +Calls `Fun(Elem)` for each `Elem` of `Set1` to update or remove +elements from `Set1`. + +`Fun/1` must return either a Boolean or a tuple `{true, Value}`. The +function returns the set of elements for which `Fun` returns a new +value, with `true` being equivalent to `{true, Elem}`. + +`gb_sets:filtermap/2` behaves as if it were defined as follows: + +```erlang +filtermap(Fun, Set1) -> + gb_sets:from_list(lists:filtermap(Fun, Set1)). +``` + +## Examples + +```erlang +> S = gb_sets:from_list([2,4,5,6,8,9]) +> F = fun(X) -> + case X rem 2 of + 0 -> {true, X div 2}; + 1 -> false + end + end. +> Set = gb_sets:filtermap(F, S). +> gb_sets:to_list(Set). +[1,2,3,4] +``` +""". -doc(#{since => <<"OTP 27.0">>}). -spec filtermap(Fun, Set1) -> Set2 when Fun :: fun((Element1) -> boolean() | {true, Element2}), @@ -1201,8 +1636,17 @@ filtermap_1({Key, Small, Big}, F, L) -> filtermap_1(nil, _F, L) -> L. -doc """ -Folds `Function` over every element in `Set` returning the final value of the -accumulator. +Folds `Function` over every element in `Set` and returns the final value of +the accumulator. + +## Examples + +```erlang +> S = gb_sets:from_list([1,2,3,4]). +> Plus = fun erlang:'+'/2. +> gb_sets:fold(Plus, 0, S). +10 +``` """. -spec fold(Function, Acc0, Set) -> Acc1 when Function :: fun((Element, AccIn) -> AccOut), diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 6ec419cf7cf..1b6ab6af717 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -52,21 +52,41 @@ sets in the Standard Library. -doc "As returned by `new/0`.". -type ordset(T) :: [T]. -%% new() -> Set. -%% Return a new empty ordered set. +-doc """ +Returns a new empty ordered set. + +## Examples --doc "Returns a new empty ordered set.". +```erlang +> ordsets:new() +[] +``` +""". -spec new() -> []. new() -> []. -%% is_set(Term) -> boolean(). -%% Return 'true' if Set is an ordered set of elements, else 'false'. - -doc """ -Returns `true` if `Ordset` is an ordered set of elements, otherwise `false`. -This function will return `true` for any ordered list, even when not constructed -by the functions in this module. +Returns `true` if `Ordset` is an ordered set of elements; otherwise, +returns `false`. + +> #### Note {: .info } +> +> This function returns true for any ordered list, even if it was not +> constructed using the functions in this module. + +## Examples + +```erlang +> ordsets:is_set(ordsets:from_list([a,x,13,{p,q}])). +true +> ordsets:is_set([a,b,c]). +true +> ordsets:is_set([z,a]). +false +> ordsets:is_set({a,b}). +false +``` """. -spec is_set(Ordset) -> boolean() when Ordset :: term(). @@ -80,30 +100,55 @@ is_set([E2|Es], E1) when E1 < E2 -> is_set([_|_], _) -> false; is_set([], _) -> true. -%% size(OrdSet) -> int(). -%% Return the number of elements in OrdSet. +-doc """ +Returns the number of elements in `Ordset`. + +## Examples --doc "Returns the number of elements in `Ordset`.". +```erlang +> ordsets:size(ordsets:new()). +0 +> ordsets:size(ordsets:from_list([4,5,6])). +3 +``` +""". -spec size(Ordset) -> non_neg_integer() when Ordset :: ordset(_). size(S) -> length(S). -%% is_empty(OrdSet) -> boolean(). -%% Return 'true' if OrdSet is an empty set, otherwise 'false'. --doc "Returns `true` if `Ordset` is an empty set, otherwise `false`.". +-doc """ +Returns `true` if `Ordset` is an empty set; otherwise, returns `false`. + +## Examples + +```erlang +> ordsets:is_empty(ordsets:new()). +true +> ordsets:is_empty(ordsets:from_list([1])). +false +``` +""". -doc(#{since => <<"OTP 21.0">>}). -spec is_empty(Ordset) -> boolean() when Ordset :: ordset(_). -is_empty(S) -> S=:=[]. +is_empty(S) -> S =:= []. -%% is_equal(OrdSet1, OrdSet2) -> boolean(). -%% Return 'true' if OrdSet1 and OrdSet2 contain the same elements, -%% otherwise 'false'. -doc """ -Returns `true` if `Ordset1` and `Ordset2` are equal, that is when every element -of one set is also a member of the respective other set, otherwise `false`. +Returns `true` if `Ordset1` and `Ordset2` are equal, that is, if every element +of one set is also a member of the other set; otherwise, returns `false`. + +## Examples + +```erlang +> Empty = ordsets:new(). +> S = ordsets:from_list([a,b]). +> ordsets:is_equal(S, S) +true +> ordsets:is_equal(S, Empty) +false +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec is_equal(Ordset1, Ordset2) -> boolean() when @@ -113,20 +158,33 @@ of one set is also a member of the respective other set, otherwise `false`. is_equal(S1, S2) when is_list(S1), is_list(S2) -> S1 == S2. -%% to_list(OrdSet) -> [Elem]. -%% Return the elements in OrdSet as a list. +-doc """ +Returns the elements of `Ordset` as a list. + +## Examples --doc "Returns the elements of `Ordset` as a list.". +```erlang +> S = ordsets:from_list([a,b]). +> ordsets:to_list(S). +[a,b] +``` +""". -spec to_list(Ordset) -> List when Ordset :: ordset(T), List :: [T]. to_list(S) -> S. -%% from_list([Elem]) -> Set. -%% Build an ordered set from the elements in List. +-doc """ +Returns an ordered set of the elements in `List`. + +## Examples --doc "Returns an ordered set of the elements in `List`.". +```erlang +> ordsets:from_list([a,b,a,b,b,c]). +[a,b,c] +``` +""". -spec from_list(List) -> Ordset when List :: [T], Ordset :: ordset(T). @@ -134,10 +192,19 @@ to_list(S) -> S. from_list(L) -> lists:usort(L). -%% is_element(Element, OrdSet) -> boolean(). -%% Return 'true' if Element is an element of OrdSet, else 'false'. +-doc """ +Returns `true` if `Element` is an element of `Ordset`; otherwise, returns `false`. + +## Examples --doc "Returns `true` if `Element` is an element of `Ordset`, otherwise `false`.". +```erlang +> S = ordsets:from_list([a,b,c]). +> ordsets:is_element(42, S). +false +> ordsets:is_element(b, S). +true +``` +""". -spec is_element(Element, Ordset) -> boolean() when Element :: term(), Ordset :: ordset(_). @@ -147,26 +214,45 @@ is_element(E, [H|_]) when E < H -> false; is_element(_E, [_H|_]) -> true; %E == H is_element(_, []) -> false. -%% add_element(Element, OrdSet) -> OrdSet. -%% Return OrdSet with Element inserted in it. - --doc "Returns a new ordered set formed from `Ordset1` with `Element` inserted.". +-doc """ +Returns a new ordered set formed from `Ordset1` with `Element` inserted. + +## Examples + +```erlang +> S0 = ordsets:new(). +[] +> S1 = ordsets:add_element(7, S0). +[7] +> S2 = ordsets:add_element(42, S1). +[7,42] +> ordsets:add_element(42, S2). +[7,42] +``` +""". -spec add_element(Element, Ordset1) -> Ordset2 when Element :: E, Ordset1 :: ordset(T), Ordset2 :: ordset(T | E). -%-spec add_element(E, ordset(T)) -> [T | E,...]. - add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)]; add_element(E, [H|_]=Set) when E < H -> [E|Set]; add_element(_E, [_H|_]=Set) -> Set; %E == H add_element(E, []) -> [E]. -%% del_element(Element, OrdSet) -> OrdSet. -%% Return OrdSet but with Element removed. +-doc """ +Returns a copy of `Ordset1` with `Element` removed. + +## Examples --doc "Returns `Ordset1`, but with `Element` removed.". +```erlang +> S = ordsets:from_list([a,b,c]). +> ordsets:del_element(c, S). +[a,b] +> ordsets:del_element(x, S). +[a,b,c] +``` +""". -spec del_element(Element, Ordset1) -> Ordset2 when Element :: term(), Ordset1 :: ordset(T), @@ -177,10 +263,21 @@ del_element(E, [H|_]=Set) when E < H -> Set; del_element(_E, [_H|Es]) -> Es; %E == H del_element(_, []) -> []. -%% union(OrdSet1, OrdSet2) -> OrdSet -%% Return the union of OrdSet1 and OrdSet2. +-doc """ +Returns the union of `Ordset1` and `Ordset2`. --doc "Returns the merged (union) set of `Ordset1` and `Ordset2`.". +The union of two sets is a new set that contains all the elements from +both sets, without duplicates. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([c,d,e,f]). +> ordsets:union(S0, S1). +[a,b,c,d,e,f] +``` +""". -spec union(Ordset1, Ordset2) -> Ordset3 when Ordset1 :: ordset(T1), Ordset2 :: ordset(T2), @@ -195,10 +292,20 @@ union([E1|Es1], [_E2|Es2]) -> %E1 == E2 union([], Es2) -> Es2; union(Es1, []) -> Es1. -%% union([OrdSet]) -> OrdSet -%% Return the union of the list of ordered sets. - --doc "Returns the merged (union) set of the list of sets.". +-doc """ +Returns the union of a list of sets. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([d,e,f]). +> S2 = ordsets:from_list([q,r]) +> Sets = [S0, S1, S2]. +> ordsets:union(Sets). +[a,b,c,d,e,f,q,r] +``` +""". -spec union(OrdsetList) -> Ordset when OrdsetList :: [ordset(T)], Ordset :: ordset(T). @@ -206,10 +313,24 @@ union(Es1, []) -> Es1. union(OrdsetList) -> lists:umerge(OrdsetList). -%% intersection(OrdSet1, OrdSet2) -> OrdSet. -%% Return the intersection of OrdSet1 and OrdSet2. - --doc "Returns the intersection of `Ordset1` and `Ordset2`.". +-doc """ +Returns the intersection of `Ordset1` and `Ordset2`. + +The intersection of two sets is a new set that contains only the +elements that are present in both sets. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([c,d,e,f]). +> S2 = ordsets:from_list([q,r]). +> ordsets:intersection(S0, S1). +[c,d] +> ordsets:intersection(S1, S2). +[] +``` +""". -spec intersection(Ordset1, Ordset2) -> Ordset3 when Ordset1 :: ordset(_), Ordset2 :: ordset(_), @@ -226,10 +347,25 @@ intersection([], _) -> intersection(_, []) -> []. -%% intersection([OrdSet]) -> OrdSet. -%% Return the intersection of the list of ordered sets. - --doc "Returns the intersection of the non-empty list of sets.". +-doc """ +Returns the intersection of the non-empty list of sets. + +The intersection of two sets is a new set that contains only the +elements that are present in both sets. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([d,e,f]). +> S2 = ordsets:from_list([q,r]) +> Sets = [S0, S1, S2]. +> ordsets:intersection([S0, S1, S2]). +[] +> ordsets:intersection([S0, S1]). +[d] +``` +""". -spec intersection(OrdsetList) -> Ordset when OrdsetList :: [ordset(_),...], Ordset :: ordset(_). @@ -242,12 +378,26 @@ intersection1(S1, [S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection1(S1, []) -> S1. -%% is_disjoint(OrdSet1, OrdSet2) -> boolean(). -%% Check whether OrdSet1 and OrdSet2 are disjoint. - -doc """ -Returns `true` if `Ordset1` and `Ordset2` are disjoint (have no elements in -common), otherwise `false`. +Returns `true` if `Ordset1` and `Ordset2` are disjoint; otherwise, +returns `false`. + +Two sets are disjoint if they have no elements in common. + +This function is equivalent to `ordsets:intersection(Ordset1, Ordset2) +=:= []`, but faster. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([d,e,f]). +> S2 = ordsets:from_list([q,r]) +> ordsets:is_disjoint(S0, S1). +false +> ordsets:is_disjoint(S1, S2). +true +``` """. -spec is_disjoint(Ordset1, Ordset2) -> boolean() when Ordset1 :: ordset(_), @@ -264,11 +414,20 @@ is_disjoint([], _) -> is_disjoint(_, []) -> true. -%% subtract(OrdSet1, OrdSet2) -> OrdSet. -%% Return all and only the elements of OrdSet1 which are not also in -%% OrdSet2. - --doc "Returns only the elements of `Ordset1` that are not also elements of `Ordset2`.". +-doc """ +Returns the elements of `Ordset1` that are not elements in `Ordset2`. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([c,d,e,f]). +> ordsets:subtract(S0, S1). +[a,b] +> ordsets:subtract(S1, S0). +[e,f] +``` +""". -spec subtract(Ordset1, Ordset2) -> Ordset3 when Ordset1 :: ordset(_), Ordset2 :: ordset(_), @@ -283,13 +442,22 @@ subtract([_E1|Es1], [_E2|Es2]) -> %E1 == E2 subtract([], _) -> []; subtract(Es1, []) -> Es1. -%% is_subset(OrdSet1, OrdSet2) -> boolean(). -%% Return 'true' when every element of OrdSet1 is also a member of -%% OrdSet2, else 'false'. - -doc """ -Returns `true` when every element of `Ordset1` is also a member of `Ordset2`, -otherwise `false`. +Returns `true` when every element of `Ordset1` is also a member of `Ordset2`; +otherwise, returns `false`. + +## Examples + +```erlang +> S0 = ordsets:from_list([a,b,c,d]). +> S1 = ordsets:from_list([c,d]). +> ordsets:is_subset(S1, S0). +true +> ordsets:is_subset(S0, S1). +false +> ordsets:is_subset(S0, S0). +true +``` """. -spec is_subset(Ordset1, Ordset2) -> boolean() when Ordset1 :: ordset(_), @@ -304,12 +472,18 @@ is_subset([_E1|Es1], [_E2|Es2]) -> %E1 == E2 is_subset([], _) -> true; is_subset(_, []) -> false. -%% fold(Fun, Accumulator, OrdSet) -> Accumulator. -%% Fold function Fun over all elements in OrdSet and return Accumulator. - -doc """ Folds `Function` over every element in `Ordset` and returns the final value of the accumulator. + +## Examples + +```erlang +> S = ordsets:from_list([1,2,3,4]). +> Plus = fun erlang:'+'/2. +> ordsets:fold(Plus, 0, S). +10 +``` """. -spec fold(Function, Acc0, Ordset) -> Acc1 when Function :: fun((Element :: T, AccIn :: term()) -> AccOut :: term()), @@ -320,10 +494,18 @@ the accumulator. fold(F, Acc, Set) -> lists:foldl(F, Acc, Set). -%% filter(Fun, OrdSet) -> OrdSet. -%% Filter OrdSet with Fun. +-doc """ +Filters elements in `Ordset1` with boolean function `Pred`. + +## Examples --doc "Filters elements in `Ordset1` with boolean function `Pred`.". +```erlang +> S = ordsets:from_list([1,2,3,4,5,6,7]). +> IsEven = fun(N) -> N rem 2 =:= 0 end. +> ordsets:filter(IsEven, S). +[2,4,6] +``` +""". -spec filter(Pred, Ordset1) -> Ordset2 when Pred :: fun((Element :: T) -> boolean()), Ordset1 :: ordset(T), @@ -335,7 +517,18 @@ filter(F, Set) -> %% map(Fun, OrdSet) -> OrdSet. %% Map OrdSet with Fun. --doc "Maps elements in `Ordset1` with mapping function `Fun`.". +-doc """ +Maps elements in `Ordset1` with mapping function `Fun`. + +## Examples + +```erlang +> S = ordsets:from_list([1,2,3,4,5,6,7]). +> F = fun(N) -> N div 2 end. +> ordsets:map(F, S). +[0,1,2,3] +``` +""". -doc(#{since => <<"OTP 27.0">>}). -spec map(Fun, Ordset1) -> Ordset2 when Fun :: fun((Element1 :: T1) -> Element2 :: T2), @@ -345,9 +538,35 @@ filter(F, Set) -> map(F, Set) -> from_list(lists:map(F, Set)). -%% filtermap(Fun, OrdSet) -> OrdSet. -%% Filter and map Ordset with Fun. --doc "Filters and maps elements in `Ordset1` with function `Fun`.". +-doc """ +Calls `Fun(Elem)` for each `Elem` of `Ordset1` to update or remove +elements from `Ordset1`. + +`Fun/1` must return either a Boolean or a tuple `{true, Value}`. The +function returns the set of elements for which `Fun` returns a new +value, with `true` being equivalent to `{true, Elem}`. + +`ordsets:filtermap/2` behaves as if it were defined as follows: + +```erlang +filtermap(Fun, Ordset1) -> + ordsets:from_list(lists:filtermap(Fun, Ordset1)). +``` + +## Examples + +```erlang +> S = ordsets:from_list([2,4,5,6,8,9]) +> F = fun(X) -> + case X rem 2 of + 0 -> {true, X div 2}; + 1 -> false + end + end. +> ordsets:filtermap(F, S). +[1,2,3,4] +``` +""". -doc(#{since => <<"OTP 27.0">>}). -spec filtermap(Fun, Ordset1) -> Ordset2 when Fun :: fun((Element1 :: T1) -> boolean | ({true, Element2 :: T2})), diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index 471cce4bbb0..b6157bdb513 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -47,10 +47,11 @@ existing Erlang terms. See note on [data types](`e:system:data_types.md#no_user_types`). Any code assuming knowledge of the format is running on thin ice. -This module provides the same interface as the `m:ordsets` module but with an -undefined representation. One difference is that while this module considers two -elements as different if they do not match (`=:=`), `ordsets` considers two -elements as different if and only if they do not compare equal (`==`). +This module provides the same interface as the `m:ordsets` module but +with an undefined representation. One key difference is that this +module considers two elements as different if they do not match +(`=:=`), whereas `ordsets` considers them different if and only if +they do not compare equal (`==`). Erlang/OTP 24.0 introduced a new more performant representation for sets which has become the default in Erlang/OTP 28. Developers can use the old representation @@ -96,7 +97,7 @@ representations. > do not match (`=:=`), while both `m:ordsets` and `m:gb_sets` consider elements > as different if and only if they do not compare equal (`==`). > -> _Example:_ +> ### Examples > > ```erlang > 1> sets:is_element(1.0, sets:from_list([1])). @@ -163,11 +164,31 @@ representations. %%------------------------------------------------------------------------------ %% new() -> Set --doc "Returns a new empty set.". +-doc """ +Returns a new empty set. + +## Examples + +```erlang +> sets:to_list(sets:new()). +[] +``` +""". -spec new() -> set(none()). new() -> #{}. --doc "Returns a new empty set at the given version.". +-doc """ +Returns a new empty set of the given version. + +## Examples + +```erlang +> sets:to_list(sets:new([{version, 1}])). +[] +> sets:new() =:= sets:new([{version, 2}]). +true +``` +""". -doc(#{since => <<"OTP 24.0">>}). -spec new([{version, 1..2}]) -> set(none()). new([{version, 2}]) -> @@ -180,16 +201,34 @@ new(Opts) -> 2 -> new() end. -%% from_list([Elem]) -> Set. -%% Build a set from the elements in List. --doc "Returns a set of the elements in `List`.". +-doc """ +Returns a set of the elements in `List`. + +## Examples + +```erlang +> S = sets:from_list([a,b,c]). +> lists:sort(sets:to_list(S)). +[a,b,c] +``` +""". -spec from_list(List) -> Set when List :: [Element], Set :: set(Element). from_list(Ls) -> maps:from_keys(Ls, ?VALUE). --doc "Returns a set of the elements in `List` at the given version.". +-doc """ +Returns a set of the elements in `List` of the given version. + +## Examples + +```erlang +> S = sets:from_list([a,b,c], [{version, 1}]). +> lists:sort(sets:to_list(S)). +[a,b,c] +``` +""". -doc(#{since => <<"OTP 24.0">>}). -spec from_list(List, [{version, 1..2}]) -> Set when List :: [Element], @@ -198,19 +237,39 @@ from_list(Ls, [{version, 2}]) -> from_list(Ls); from_list(Ls, Opts) -> case proplists:get_value(version, Opts, 2) of - 1 -> lists:foldl(fun (E, S) -> add_element(E, S) end, new([{version, 1}]), Ls); - 2 -> from_list(Ls) + 1 -> + lists:foldl(fun (E, S) -> + add_element(E, S) + end, new([{version, 1}]), Ls); + 2 -> + from_list(Ls) end. %%------------------------------------------------------------------------------ -%% is_set(Set) -> boolean(). -%% Return 'true' if Set is a set of elements, else 'false'. -doc """ -Returns `true` if `Set` appears to be a set of elements, otherwise `false`. +Returns `true` if `Set` appears to be a set of elements; otherwise, +returns `false`. -Note that the test is shallow and will return `true` for any term that coincides with -the possible representations of a set. See also note on [data types](`e:system:data_types.md#no_user_types`). +> #### Note {: .info } +> +> Note that the test is shallow and will return `true` for any term that +> coincides with the possible representations of a set. See also note on +> [data types](`e:system:data_types.md#no_user_types`). +> +> Furthermore, since sets are opaque, calling this function on terms +> that are not sets could result in `m:dialyzer` warnings. + +## Examples + +```erlang +> sets:is_set(sets:new()). +true +> sets:is_set(sets:new([{version,1}])). +true +> sets:is_set(42). +false +``` """. -spec is_set(Set) -> boolean() when Set :: term(). @@ -218,29 +277,58 @@ is_set(#{}) -> true; is_set(#set{}) -> true; is_set(_) -> false. -%% size(Set) -> int(). -%% Return the number of elements in Set. --doc "Returns the number of elements in `Set`.". +-doc """ +Returns the number of elements in `Set`. + +## Examples + +```erlang +> sets:size(sets:new()). +0 +> sets:size(sets:from_list([4,5,6])). +3 +``` +""". -spec size(Set) -> non_neg_integer() when Set :: set(). size(#{}=S) -> map_size(S); size(#set{size=Size}) -> Size. -%% is_empty(Set) -> boolean(). -%% Return 'true' if Set is an empty set, otherwise 'false'. --doc "Returns `true` if `Set` is an empty set, otherwise `false`.". +-doc """ +Returns `true` if `Set` is an empty set; otherwise, returns `false`. + +## Examples + +```erlang +> sets:is_empty(sets:new()). +true +> sets:is_empty(sets:from_list([1])). +false +``` +""". -doc(#{since => <<"OTP 21.0">>}). -spec is_empty(Set) -> boolean() when Set :: set(). -is_empty(#{}=S) -> map_size(S)=:=0; -is_empty(#set{size=Size}) -> Size=:=0. +is_empty(#{}=S) -> map_size(S) =:= 0; +is_empty(#set{size=Size}) -> Size =:= 0. -%% is_equal(Set1, Set2) -> boolean(). -%% Return 'true' if Set1 and Set2 contain the same elements, -%% otherwise 'false'. -doc """ -Returns `true` if `Set1` and `Set2` are equal, that is when every element of one -set is also a member of the respective other set, otherwise `false`. +Returns `true` if `Set1` and `Set2` are equal, that is, if every element +of one set is also a member of the other set; otherwise, returns `false`. + +## Examples + +```erlang +> Empty = sets:new(). +> S = sets:from_list([a,b]). +> sets:is_equal(S, S) +true +> sets:is_equal(S, Empty) +false +> OldSet = sets:from_list([a,b], [{version,1}]). +> sets:is_equal(S, OldSet). +true +``` """. -doc(#{since => <<"OTP 27.0">>}). -spec is_equal(Set1, Set2) -> boolean() when @@ -259,11 +347,18 @@ is_equal(S1, S2) -> canonicalize_v2(S) -> from_list(to_list(S)). -%% to_list(Set) -> [Elem]. -%% Return the elements in Set as a list. -doc """ -Returns the elements of `Set` as a list. The order of the returned elements is -undefined. +Returns the elements of `Set` as a list. + +The order of the returned elements is undefined. + +## Examples + +```erlang +> S = sets:from_list([1,2,3]). +> lists:sort(sets:to_list(S)). +[1,2,3] +``` """. -spec to_list(Set) -> List when Set :: set(Element), @@ -273,9 +368,20 @@ to_list(#{}=S) -> to_list(#set{} = S) -> fold(fun (Elem, List) -> [Elem|List] end, [], S). -%% is_element(Element, Set) -> boolean(). -%% Return 'true' if Element is an element of Set, else 'false'. --doc "Returns `true` if `Element` is an element of `Set`, otherwise `false`.". +-doc """ +Returns `true` if `Element` is an element of `Set`; otherwise, returns +`false`. + +## Examples + +```erlang +> S = sets:from_list([a,b,c]). +> sets:is_element(42, S). +false +> sets:is_element(b, S). +true +``` +""". -spec is_element(Element, Set) -> boolean() when Set :: set(Element). is_element(E, #{}=S) -> @@ -288,9 +394,24 @@ is_element(E, #set{}=S) -> Bkt = get_bucket(S, Slot), lists:member(E, Bkt). -%% add_element(Element, Set) -> Set. -%% Return Set with Element inserted in it. --doc "Returns a new set formed from `Set1` with `Element` inserted.". +-doc """ +Returns a new set formed from `Set1` with `Element` inserted. + +## Examples + +```erlang +> S0 = sets:new(). +> S1 = sets:add_element(7, S0). +> sets:to_list(S1). +[7] +> S2 = sets:add_element(42, S1). +> lists:sort(sets:to_list(S2)). +[7,42] +> S2 = sets:add_element(42, S1). +> lists:sort(sets:to_list(S2)). +[7,42] +``` +""". -spec add_element(Element, Set1) -> Set2 when Set1 :: set(Element), Set2 :: set(Element). @@ -307,9 +428,20 @@ add_element(E, #set{}=S0) -> maybe_expand(S1) end. -%% del_element(Element, Set) -> Set. -%% Return Set but with Element removed. --doc "Returns `Set1`, but with `Element` removed.". +-doc """ +Returns a copy of `Set1` with `Element` removed. + +## Examples + +```erlang +> S = sets:from_list([a,b]). +> sets:to_list(sets:del_element(b, S)). +[a] +> S = sets:del_element(x, S). +> lists:sort(sets:to_list(S)). +[a,b] +``` +""". -spec del_element(Element, Set1) -> Set2 when Set1 :: set(Element), Set2 :: set(Element). @@ -340,15 +472,28 @@ update_bucket(Set, Slot, NewBucket) -> Seg = element(SegI, Segs), Set#set{segs = setelement(SegI, Segs, setelement(BktI, Seg, NewBucket))}. -%% union(Set1, Set2) -> Set -%% Return the union of Set1 and Set2. --doc "Returns the merged (union) set of `Set1` and `Set2`.". +-doc """ +Returns the union of `Set1` and `Set2`. + +The union of two sets is a new set that contains all the elements from +both sets, without duplicates. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([c,d,e,f]). +> Union = sets:union(S0, S1). +> lists:sort(sets:to_list(Union)). +[a,b,c,d,e,f] +``` +""". -spec union(Set1, Set2) -> Set3 when Set1 :: set(Element), Set2 :: set(Element), Set3 :: set(Element). union(#{}=S1, #{}=S2) -> - maps:merge(S1,S2); + maps:merge(S1, S2); union(S1, S2) -> case size(S1) < size(S2) of true -> @@ -357,9 +502,21 @@ union(S1, S2) -> fold(fun (E, S) -> add_element(E, S) end, S1, S2) end. -%% union([Set]) -> Set -%% Return the union of the list of sets. --doc "Returns the merged (union) set of the list of sets.". +-doc """ +Returns the union of a list of sets. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([d,e,f]). +> S2 = sets:from_list([q,r]) +> Sets = [S0, S1, S2]. +> Union = sets:union(Sets). +> lists:sort(sets:to_list(Union)). +[a,b,c,d,e,f,q,r] +``` +""". -spec union(SetList) -> Set when SetList :: [set(Element)], Set :: set(Element). @@ -373,9 +530,25 @@ union1(S1, [S2|Ss]) -> union1(union(S1, S2), Ss); union1(S1, []) -> S1. -%% intersection(Set1, Set2) -> Set. -%% Return the intersection of Set1 and Set2. --doc "Returns the intersection of `Set1` and `Set2`.". +-doc """ +Returns the intersection of `Set1` and `Set2`. + +The intersection of two sets is a new set that contains only the +elements that are present in both sets. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([c,d,e,f]). +> S2 = sets:from_list([q,r]). +> Intersection = sets:intersection(S0, S1). +> lists:sort(sets:to_list(Intersection)). +[c,d] +> sets:to_list(sets:intersection(S1, S2)). +[] +``` +""". -spec intersection(Set1, Set2) -> Set3 when Set1 :: set(Element), Set2 :: set(Element), @@ -384,10 +557,12 @@ intersection(#{}=S1, #{}=S2) -> case map_size(S1) < map_size(S2) of true -> Next = maps:next(maps:iterator(S1)), - intersection_heuristic(Next, [], [], floor(map_size(S1) * 0.75), S1, S2); + intersection_heuristic(Next, [], [], + floor(map_size(S1) * 0.75), S1, S2); false -> Next = maps:next(maps:iterator(S2)), - intersection_heuristic(Next, [], [], floor(map_size(S2) * 0.75), S2, S1) + intersection_heuristic(Next, [], [], + floor(map_size(S2) * 0.75), S2, S1) end; intersection(S1, S2) -> case size(S1) < size(S2) of @@ -400,23 +575,26 @@ intersection(S1, S2) -> %% If we are keeping more than 75% of the keys, then it is %% cheaper to delete them. Stop accumulating and start deleting. intersection_heuristic(Next, _Keep, Delete, 0, Acc, Reference) -> - intersection_decided(Next, remove_keys(Delete, Acc), Reference); -intersection_heuristic({Key, _Value, Iterator}, Keep, Delete, KeepCount, Acc, Reference) -> + intersection_decided(Next, remove_keys(Delete, Acc), Reference); +intersection_heuristic({Key, _Value, Iterator}, Keep, Delete, KeepCount, + Acc, Reference) -> Next = maps:next(Iterator), case Reference of #{Key := _} -> - intersection_heuristic(Next, [Key | Keep], Delete, KeepCount - 1, Acc, Reference); - _ -> - intersection_heuristic(Next, Keep, [Key | Delete], KeepCount, Acc, Reference) + intersection_heuristic(Next, [Key | Keep], Delete, KeepCount - 1, + Acc, Reference); + #{} -> + intersection_heuristic(Next, Keep, [Key | Delete], KeepCount, + Acc, Reference) end; intersection_heuristic(none, Keep, _Delete, _Count, _Acc, _Reference) -> maps:from_keys(Keep, ?VALUE). intersection_decided({Key, _Value, Iterator}, Acc0, Reference) -> Acc1 = case Reference of - #{Key := _} -> Acc0; - #{} -> maps:remove(Key, Acc0) - end, + #{Key := _} -> Acc0; + #{} -> maps:remove(Key, Acc0) + end, intersection_decided(maps:next(Iterator), Acc1, Reference); intersection_decided(none, Acc, _Reference) -> Acc. @@ -424,9 +602,25 @@ intersection_decided(none, Acc, _Reference) -> remove_keys([K | Ks], Map) -> remove_keys(Ks, maps:remove(K, Map)); remove_keys([], Map) -> Map. -%% intersection([Set]) -> Set. -%% Return the intersection of the list of sets. --doc "Returns the intersection of the non-empty list of sets.". +-doc """ +Returns the intersection of the non-empty list of sets. + +The intersection of two sets is a new set that contains only the +elements that are present in both sets. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([d,e,f]). +> S2 = sets:from_list([q,r]) +> Sets = [S0, S1, S2]. +> sets:to_list(sets:intersection([S0, S1, S2])). +[] +> sets:to_list(sets:intersection([S0, S1])). +[d] +``` +""". -spec intersection(SetList) -> Set when SetList :: [set(Element),...], Set :: set(Element). @@ -439,11 +633,26 @@ intersection1(S1, [S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection1(S1, []) -> S1. -%% is_disjoint(Set1, Set2) -> boolean(). -%% Check whether Set1 and Set2 are disjoint. -doc """ -Returns `true` if `Set1` and `Set2` are disjoint (have no elements in common), -otherwise `false`. +Returns `true` if `Set1` and `Set2` are disjoint; otherwise, returns +`false`. + +Two sets are disjoint if they have no elements in common. + +This function is equivalent to `sets:intersection(Set1, Set2) =:= []`, +but faster. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([d,e,f]). +> S2 = sets:from_list([q,r]) +> sets:is_disjoint(S0, S1). +false +> sets:is_disjoint(S1, S2). +true +``` """. -spec is_disjoint(Set1, Set2) -> boolean() when Set1 :: set(Element), @@ -478,10 +687,20 @@ is_disjoint_1(Set, Iter) -> true end. -%% subtract(Set1, Set2) -> Set. -%% Return all and only the elements of Set1 which are not also in -%% Set2. --doc "Returns only the elements of `Set1` that are not also elements of `Set2`.". +-doc """ +Returns the elements of `Set1` that are not elements in `Set2`. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([c,d,e,f]). +> lists:sort(sets:to_list(sets:subtract(S0, S1))). +[a,b] +> lists:sort(sets:to_list(sets:subtract(S1, S0))). +[e,f] +``` +""". -spec subtract(Set1, Set2) -> Set3 when Set1 :: set(Element), Set2 :: set(Element), @@ -538,12 +757,22 @@ subtract_decided({Key, _Value, Iterator}, Acc, Reference) -> subtract_decided(none, Acc, _Reference) -> Acc. -%% is_subset(Set1, Set2) -> boolean(). -%% Return 'true' when every element of Set1 is also a member of -%% Set2, else 'false'. -doc """ -Returns `true` when every element of `Set1` is also a member of `Set2`, -otherwise `false`. +Returns `true` when every element of `Set1` is also a member of `Set2`; +otherwise, returns `false`. + +## Examples + +```erlang +> S0 = sets:from_list([a,b,c,d]). +> S1 = sets:from_list([c,d]). +> sets:is_subset(S1, S0). +true +> sets:is_subset(S0, S1). +false +> sets:is_subset(S0, S0). +true +``` """. -spec is_subset(Set1, Set2) -> boolean() when Set1 :: set(Element), @@ -570,11 +799,20 @@ is_subset_1(Set, Iter) -> true end. -%% fold(Fun, Accumulator, Set) -> Accumulator. -%% Fold function Fun over all elements in Set and return Accumulator. -doc """ -Folds `Function` over every element in `Set` and returns the final value of the -accumulator. The evaluation order is undefined. +Folds `Function` over every element in `Set` and returns the final value of +the accumulator. + +The evaluation order is undefined. + +## Examples + +```erlang +> S = sets:from_list([1,2,3,4]). +> Plus = fun erlang:'+'/2. +> sets:fold(Plus, 0, S). +10 +``` """. -spec fold(Function, Acc0, Set) -> Acc1 when Function :: fun((Element, AccIn) -> AccOut), @@ -596,9 +834,19 @@ fold_1(Fun, Acc, Iter) -> Acc end. -%% filter(Fun, Set) -> Set. -%% Filter Set with Fun. --doc "Filters elements in `Set1` with boolean function `Pred`.". +-doc """ +Filters elements in `Set1` with boolean function `Pred`. + +## Examples + +```erlang +> S = sets:from_list([1,2,3,4,5,6,7]). +> IsEven = fun(N) -> N rem 2 =:= 0 end. +> Filtered = sets:filter(IsEven, S). +> lists:sort(sets:to_list(Filtered)). +[2,4,6] +``` +""". -spec filter(Pred, Set1) -> Set2 when Pred :: fun((Element) -> boolean()), Set1 :: set(Element), @@ -610,9 +858,19 @@ filter(F, #{}=D) when is_function(F, 1)-> filter(F, #set{}=D) when is_function(F, 1)-> filter_set(F, D). -%% map(Fun, Set) -> Set. -%% Map Set with Map. --doc "Maps elements in `Set1` with mapping function `Fun`.". +-doc """ +Maps elements in `Set1` with mapping function `Fun`. + +## Examples + +```erlang +> S = sets:from_list([1,2,3,4,5,6,7]). +> F = fun(N) -> N div 2 end. +> Mapped = sets:map(F, S). +> lists:sort(sets:to_list(Mapped)). +[0,1,2,3] +``` +""". -doc(#{since => <<"OTP 27.0">>}). -spec map(Fun, Set1) -> Set2 when Fun :: fun((Element1) -> Element2), @@ -620,16 +878,43 @@ filter(F, #set{}=D) when is_function(F, 1)-> Set2 :: set(Element2). map(F, #{}=D) when is_function(F, 1) -> %% For this purpose, it is more efficient to use - %% maps:from_keys than a map comprehension. + %% maps:from_keys/2 than a map comprehension. maps:from_keys([F(K) || K := _ <- D], ?VALUE); map(F, #set{}=D) when is_function(F, 1) -> fold(fun(E, Acc) -> add_element(F(E), Acc) end, new([{version, 1}]), D). -%% filtermap(Fun, Set) -> Set. -%% Filter and map Set with Fun. --doc "Filters and maps elements in `Set1` with function `Fun`.". +-doc """ +Calls `Fun(Elem)` for each `Elem` of `Set1` to update or remove +elements from `Set1`. + +`Fun/1` must return either a Boolean or a tuple `{true, Value}`. The +function returns the set of elements for which `Fun` returns a new +value, with `true` being equivalent to `{true, Elem}`. + +`sets:filtermap/2` behaves as if it were defined as follows: + +```erlang +filtermap(Fun, Set1) -> + sets:from_list(lists:filtermap(Fun, Set1)). +``` + +## Examples + +```erlang +> S = sets:from_list([2,4,5,6,8,9]) +> F = fun(X) -> + case X rem 2 of + 0 -> {true, X div 2}; + 1 -> false + end + end. +> Set = sets:filtermap(F, S). +> lists:sort(sets:to_list(Set)). +[1,2,3,4] +``` +""". -doc(#{since => <<"OTP 27.0">>}). -spec filtermap(Fun, Set1) -> Set2 when Fun :: fun((Element1) -> boolean() | {true, Element2}), diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl index 733767d37b1..37328e29d94 100644 --- a/lib/stdlib/test/sets_SUITE.erl +++ b/lib/stdlib/test/sets_SUITE.erl @@ -29,7 +29,8 @@ create/1,add_element/1,del_element/1, subtract/1,intersection/1,union/1,is_subset/1, is_equal/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]). + map/1, filtermap/1, take_smallest/1,take_largest/1, iterate/1, + doctests/1]). -include_lib("common_test/include/ct.hrl"). @@ -49,7 +50,7 @@ all() -> [create, add_element, del_element, subtract, intersection, union, is_subset, is_set, fold, filter, map, filtermap, take_smallest, take_largest, iterate, is_empty, - is_disjoint, is_equal]. + is_disjoint, is_equal, doctests]. groups() -> []. @@ -535,6 +536,14 @@ iterate_set_1(_, none, R) -> iterate_set_1(M, {E, I}, R) -> iterate_set_1(M, M(next, I), [E | R]). +doctests(_Config) -> + Modules = [gb_sets, ordsets, sets], + lists:foreach(fun(M) -> + io:format("Testing module: ~p\n", [M]), + shell_docs:test(M, []), + io:nl() + end, Modules). + %%% %%% Helper functions. %%%