Packages

sets-like wrapper based on maps

Current section

Files

Jump to
map_sets src map_sets.erl
Raw

src/map_sets.erl

%% s/sets/map_sets/g
%% Why? Because spead (This module piggybacks on `maps' module's NIFs)
-module(map_sets).
-export([new/0,is_set/1,size/1,to_list/1,from_list/1]).
-export([is_element/2,add_element/2,del_element/2]).
-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_type([set/1, set/0]).
-type set(Key) :: #{Key => term()}.
-type set() :: set(term()).
-define(UNUSED, unused).
-spec new() -> set().
new() ->
#{}.
-spec is_set(term()) -> boolean().
is_set(A) ->
is_map(A).
-spec size(set()) -> non_neg_integer().
size(A) ->
maps:size(A).
-spec fold(Function, Acc, Set) -> Acc when
Function :: fun((Element, Acc) -> Acc),
Set :: set(Element),
Acc :: term().
fold(Fun, A, B) ->
maps:fold( fun(K, _, Acc) -> Fun(K, Acc) end
, A
, B).
-spec filter(Predicate, Set) -> Set when
Predicate :: fun((Element) -> boolean()),
Set :: set(Element).
filter(P, A) ->
maps:filter( fun(K, _) -> P(K) end
, A).
-spec to_list(set(Elem)) -> [Elem].
to_list(A) ->
maps:keys(A).
-spec from_list([Elem]) -> set(Elem).
from_list(L) ->
maps:from_list([{I, ?UNUSED} || I <- L]).
-spec is_element(Elem, set(Elem)) -> boolean().
is_element(Elem, Set) ->
maps:is_key(Elem, Set).
-spec add_element(Elem, set(Elem)) -> set(Elem).
add_element(Elem, Set) ->
Set#{Elem => ?UNUSED}.
-spec del_element(Elem, set(Elem)) -> set(Elem).
del_element(Elem, Set) ->
maps:remove(Elem, Set).
-spec is_subset(set(Elem), set(Elem)) -> boolean().
is_subset(S1, S2) ->
try
[true = is_element(E, S2) || E <- to_list(S1)],
true
catch
_:{badmatch, false} ->
false
end.
-spec subtract(set(Elem), set(Elem)) -> set(Elem).
subtract(S1, S2) ->
maps:without(maps:keys(S2), S1).
-spec union(set(Elem), set(Elem)) -> set(Elem).
union(S1, S2) ->
maps:merge(S1, S2).
-spec union([set(Elem)]) -> set(Elem).
union(L) ->
lists:foldl(fun maps:merge/2, #{}, L).
-spec intersection(set(Elem), set(Elem)) -> set(Elem).
intersection(S1, S2) ->
case maps:size(S1) > maps:size(S2) of
true ->
intersection_(S1, S2);
false ->
intersection_(S2, S1)
end.
intersection_(Large, Small) ->
lists:foldl( fun(E, Acc) ->
case Large of
#{E := _} ->
Acc;
_ ->
maps:remove(E, Acc)
end
end
, Small
, to_list(Small)).
-spec intersection(nonempty_list(set(Elem))) -> set(Elem).
intersection([H|T]) ->
lists:foldl(fun intersection/2, H, T).
-spec is_disjoint(set(Elem), set(Elem)) -> boolean().
is_disjoint(S1, S2) ->
case maps:size(S1) > maps:size(S2) of
true ->
is_disjoint_(S1, S2);
false ->
is_disjoint_(S2, S1)
end.
is_disjoint_(Large, Small) ->
try
[false = maps:is_key(I, Large) || I <- to_list(Small)],
true
catch
_:{badmatch, true} ->
false
end.