diff options
Diffstat (limited to 'lib/stdlib')
-rw-r--r-- | lib/stdlib/doc/src/maps.xml | 52 | ||||
-rw-r--r-- | lib/stdlib/src/maps.erl | 61 | ||||
-rw-r--r-- | lib/stdlib/test/maps_SUITE.erl | 39 |
3 files changed, 36 insertions, 116 deletions
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml index a3afbf8e7f..987d92989d 100644 --- a/lib/stdlib/doc/src/maps.xml +++ b/lib/stdlib/doc/src/maps.xml @@ -38,8 +38,7 @@ <name name="iterator"/> <desc> <p>An iterator representing the key value associations in a map.</p> - <p>Created using <seealso marker="#iterator-1"><c>maps:iterator/1</c></seealso> - and <seealso marker="#iterator-2"><c>maps:iterator/2</c></seealso>.</p> + <p>Created using <seealso marker="#iterator-1"><c>maps:iterator/1</c></seealso>.</p> <p>Consumed by <seealso marker="#next-1"><c>maps:next/1</c></seealso>, <seealso marker="#filter-2"><c>maps:filter/2</c></seealso>, <seealso marker="#fold-3"><c>maps:fold/3</c></seealso> and @@ -194,9 +193,9 @@ false</code> <desc> <p>Returns a map iterator <c><anno>Iterator</anno></c> that can be used by <seealso marker="#next-1"><c>maps:next/1</c></seealso> - to traverse the keys and values in a map. This call is equivalent - to calling <seealso marker="#iterator-2"><c>maps:iterator/2</c></seealso> - with an empty map as the options.</p> + to traverse the key-value associations in a map. When iterating + over a map, the memory usage is guaranteed to be bounded no matter + the size of the map.</p> <p>The call fails with a <c>{badmap,Map}</c> exception if <c><anno>Map</anno></c> is not a map.</p> <p><em>Example:</em></p> @@ -215,49 +214,6 @@ none</code> </func> <func> - <name name="iterator" arity="2"/> - <fsummary>Create a map iterator.</fsummary> - <desc> - <p>Returns a map iterator <c><anno>Iterator</anno></c> that can - be used by <seealso marker="#next-1"><c>maps:next/1</c></seealso> - to traverse the keys and values in a map. The iterator will behave - according to the given <c><anno>Opts</anno></c>.</p> - <taglist> - <tag>optimize</tag> - <item> - Configures whether the iterator should be optimized for <c>speed</c> - or for <c>memory</c> consumption. Default is <c>speed</c>. - </item> - <tag>ordered</tag> - <item> - Configures whether the iterator should return the key value - associations ordered on the keys according to erlang term order - or not. The default is <c>false</c>. - </item> - </taglist> - <p>Both options can be configured at the same time.</p> - <p>The call fails with a <c>{badmap,Map}</c> exception if - <c><anno>Map</anno></c> is not a map.</p> - <p><em>Example:</em></p> - <code type="none"> -> M = maps:from_list([{I,I} || I <- lists:seq(1,50)]). -#{45 => 45,6 => 6,2 => 2,49 => 49,41 => 41,33 => 33,42 => 42, - 43 => 43,10 => 10,9 => 9,19 => 19,14 => 14,5 => 5,18 => 18, - 31 => 31,22 => 22,29 => 29,21 => 21,27 => 27,24 => 24, - 47 => 47,40 => 40,30 => 30,23 => 23,28 => 28,46 => 46, - 16 => 16,38 => 38,4 => 4,...} -> MemoryIter = maps:iterator(M, #{ optimize => memory }), ok. -ok -> {K1, V1, _} = maps:next(MemoryIter), {K1, V1}. -{46,46} -> OrderedIter = maps:iterator(M, #{ ordered => true, optimize => memory }), ok. -ok -> {K2, V2, _} = maps:next(OrderedIter), {K2, V2}. -{1,1}</code> - </desc> - </func> - - <func> <name name="keys" arity="1"/> <fsummary></fsummary> <desc> diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 2f2dd41979..a13f340709 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -24,7 +24,7 @@ map/2, size/1, update_with/3, update_with/4, without/2, with/2, - iterator/1, iterator/2, next/1]). + iterator/1, next/1]). %% BIFs -export([get/2, find/2, from_list/1, @@ -32,12 +32,14 @@ new/0, put/3, remove/2, take/2, to_list/1, update/3, values/1]). --opaque iterator() :: {non_neg_integer() | none | list(term()), map()} - | list({term(), term()}). +-opaque iterator() :: {term(), term(), iterator()} + | none | nonempty_improper_list(integer(),map()). -export_type([iterator/0]). --define(IS_ITERATOR(I), (is_list(I) orelse (is_map(element(2,I) andalso (is_list(element(1,I) orelse is_integer(element(1,I)))))))). +-dialyzer({no_improper_lists, iterator/1}). + +-define(IS_ITERATOR(I), is_tuple(I) andalso tuple_size(I) == 3; I == none; is_integer(hd(I)) andalso is_map(tl(I))). %% Shadowed by erl_bif_types: maps:get/2 -spec get(Key,Map) -> Value when @@ -121,14 +123,20 @@ remove(_,_) -> erlang:nif_error(undef). take(_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:to_list/1 -spec to_list(Map) -> [{Key,Value}] when Map :: map(), Key :: term(), Value :: term(). -to_list(_) -> erlang:nif_error(undef). +to_list(Map) when is_map(Map) -> + to_list_internal(erts_internal:map_next(0, Map, [])); +to_list(Map) -> + erlang:error({badmap,Map},[Map]). +to_list_internal([Iter, Map | Acc]) when is_integer(Iter) -> + to_list_internal(erts_internal:map_next(Iter, Map, Acc)); +to_list_internal(Acc) -> + Acc. %% Shadowed by erl_bif_types: maps:update/3 -spec update(Key,Value,Map1) -> Map2 when @@ -207,7 +215,7 @@ get(Key,Map,Default) -> Map :: map(). filter(Pred,Map) when is_function(Pred,2), is_map(Map) -> - maps:from_list([{K,V}||{K,V}<-maps:to_list(Map),Pred(K,V)]); + maps:from_list(filter_1(Pred, iterator(Map))); filter(Pred,Iterator) when is_function(Pred,2), ?IS_ITERATOR(Iterator) -> maps:from_list(filter_1(Pred, Iterator)); filter(Pred,Map) -> @@ -237,7 +245,7 @@ filter_1(Pred, Iter) -> V :: term(). fold(Fun,Init,Map) when is_function(Fun,3), is_map(Map) -> - lists:foldl(fun({K,V},A) -> Fun(K,V,A) end,Init,maps:to_list(Map)); + fold_1(Fun,Init,iterator(Map)); fold(Fun,Init,Iterator) when is_function(Fun,3), ?IS_ITERATOR(Iterator) -> fold_1(Fun,Init,Iterator); fold(Fun,Init,Map) -> @@ -260,7 +268,7 @@ fold_1(Fun, Acc, Iter) -> V2 :: term(). map(Fun,Map) when is_function(Fun, 2), is_map(Map) -> - maps:from_list([{K,Fun(K,V)}||{K,V}<-maps:to_list(Map)]); + maps:from_list(map_1(Fun, iterator(Map))); map(Fun,Iterator) when is_function(Fun, 2), ?IS_ITERATOR(Iterator) -> maps:from_list(map_1(Fun, Iterator)); map(Fun,Map) -> @@ -286,42 +294,19 @@ size(Val) -> Map :: map(), Iterator :: iterator(). -iterator(M) when is_map(M) -> iterator(M, #{}); +iterator(M) when is_map(M) -> [0 | M]; iterator(M) -> erlang:error({badmap, M}, [M]). --spec iterator(Map, Opts) -> Iterator when - Map :: map(), - Opts :: #{ ordered => boolean(), optimize => speed | memory }, - Iterator :: iterator(). -iterator(M, Opts) when is_map(M), is_map(Opts) -> - case maps:merge(default_iterator_opts(), Opts) of - #{ ordered := true, optimize := speed} -> lists:sort(maps:to_list(M)); - #{ ordered := false, optimize := speed} -> maps:to_list(M); - #{ ordered := true, optimize := memory} -> {lists:sort(maps:keys(M)), M}; - #{ ordered := false, optimize := memory} -> {0, M} - end; -iterator(M, Opts) when is_map(M) -> - erlang:error({badmap, M}, [M, Opts]); -iterator(M, Opts) -> - erlang:error(error_type(M), [M, Opts]). - -default_iterator_opts() -> - #{ ordered => false, optimize => speed }. - -spec next(Iterator) -> {Key, Value, NextIterator} | 'none' when Iterator :: iterator(), Key :: term(), Value :: term(), NextIterator :: iterator(). -next([{K, V} | T]) -> - {K, V, T}; -next([]) -> - none; -next({I, M}) when is_map(M), is_integer(I) orelse is_atom(I) -> - erts_internal:map_next(I, M); -next({[K | T], M}) when is_map(M) -> - {K, maps:get(K, M), {T, M}}; -next({[], M}) when is_map(M) -> +next({K, V, I}) -> + {K, V, I}; +next([Path | Map]) when is_integer(Path), is_map(Map) -> + erts_internal:map_next(Path, Map, iterator); +next(none) -> none; next(Iter) -> erlang:error(badarg, [Iter]). diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl index 0021dd0657..a75751b31d 100644 --- a/lib/stdlib/test/maps_SUITE.erl +++ b/lib/stdlib/test/maps_SUITE.erl @@ -30,7 +30,7 @@ -export([t_update_with_3/1, t_update_with_4/1, t_get_3/1, t_filter_2/1, t_fold_3/1,t_map_2/1,t_size_1/1, - t_iterator_1/1, t_iterator_2/1, + t_iterator_1/1, t_with_2/1,t_without_2/1]). %%-define(badmap(V,F,Args), {'EXIT', {{badmap,V}, [{maps,F,Args,_}|_]}}). @@ -48,7 +48,7 @@ all() -> [t_update_with_3,t_update_with_4, t_get_3,t_filter_2, t_fold_3,t_map_2,t_size_1, - t_iterator_1, t_iterator_2, + t_iterator_1, t_with_2,t_without_2]. t_update_with_3(Config) when is_list(Config) -> @@ -179,40 +179,19 @@ t_iterator_1(Config) when is_list(Config) -> %% Large map test - Vs = lists:seq(1,200), - M2 = maps:from_list([{{k,I},I}||I<-Vs]), + Vs2 = lists:seq(1,200), + M2 = maps:from_list([{{k,I},I}||I<-Vs2]), KVList2 = lists:sort(iter_kv(maps:iterator(M2))), KVList2 = lists:sort(maps:to_list(M2)), - ok. -t_iterator_2(Config) when is_list(Config) -> - - Vs = lists:seq(1,200), - Maps = [#{ a => 1, b => 2 }, maps:from_list([{{k,I},I}||I<-Vs])], - Optimize = [speed, memory], - Ordered = [true, false], - - [test_iterator(Map, Opt, Ord) || Map <- Maps, - Opt <- Optimize, - Ord <- Ordered], + %% Larger map test + Vs3 = lists:seq(1,10000), + M3 = maps:from_list([{{k,I},I}||I<-Vs3]), + KVList3 = lists:sort(iter_kv(maps:iterator(M3))), + KVList3 = lists:sort(maps:to_list(M3)), ok. -test_iterator(Map, Opt, Ord) -> - Opts = #{ optimize => Opt, ordered => Ord }, - test_iterator_1(maps:iterator(Map, Opts), Map, Opts). - -test_iterator_1(Iter, OrigMap, Opts) -> - erlang:display(Opts), - KVs = iter_kv(Iter), - case Opts of - #{ ordered := true } -> - KVs = lists:sort(maps:to_list(OrigMap)); - #{ ordered := false } -> - OrderedKVs = lists:sort(KVs), - OrderedKVs = lists:sort(maps:to_list(OrigMap)) - end. - iter_kv(I) -> case maps:next(I) of none -> |