From b08ef4d61e0dde98995ec3d2f69a4447255e79ef Mon Sep 17 00:00:00 2001 From: Yaroslav Rogov Date: Tue, 14 Sep 2021 11:09:50 +0300 Subject: [PATCH] Add genlib_map:search/2 (#36) * feat: Add genlib_map:search/2 * deps: Update tools deps and fix formatting * fix: Change return type --- rebar.config | 6 ++--- src/genlib_format.erl | 8 +++--- src/genlib_map.erl | 15 ++++++++++++ test/genlib_pmap_tests.erl | 2 +- test/prop_genlib_map.erl | 50 +++++++++++++++++++++++++++++++++++--- 5 files changed, 70 insertions(+), 11 deletions(-) diff --git a/rebar.config b/rebar.config index b8fde48..bb775cc 100644 --- a/rebar.config +++ b/rebar.config @@ -39,9 +39,9 @@ ]}. {plugins, [ - {erlfmt, "0.8.0"}, - {rebar3_proper, "0.12.0"}, - {rebar3_lint, "0.4.0"} + {erlfmt, "1.0.0"}, + {rebar3_proper, "0.12.1"}, + {rebar3_lint, "0.5.0"} ]}. {erlfmt, [ diff --git a/src/genlib_format.erl b/src/genlib_format.erl index 19568c2..baaf651 100644 --- a/src/genlib_format.erl +++ b/src/genlib_format.erl @@ -240,11 +240,13 @@ binary_to_hex(V, false) -> binary_to_hex_(V, A) -> << - <<(if + << + (if C >= 10 -> C + A - 10; true -> C + $0 - end)>> - || <> <= V + end) + >> + || <> <= V >>. -spec hex_to_binary(binary()) -> binary(). diff --git a/src/genlib_map.erl b/src/genlib_map.erl index 1626945..48274bc 100644 --- a/src/genlib_map.erl +++ b/src/genlib_map.erl @@ -18,6 +18,7 @@ -export([binarize/2]). -export([diff/2]). -export([fold_while/3]). +-export([search/2]). %% @@ -161,3 +162,17 @@ do_fold_while(Fun, Acc, Iter) -> {cont, NextAcc} -> do_fold_while(Fun, NextAcc, NextIter) end end. + +%% @doc Like lists:search, but for maps to reduce memory pressure +%% (comparing to naive implementation with conversion to list) +-spec search(fun((K, V) -> boolean()), #{K => V}) -> false | {K, V}. +search(Fun, Map) when is_function(Fun, 2), is_map(Map) -> + do_search(Fun, maps:next(maps:iterator(Map))). + +do_search(_Fun, none) -> + false; +do_search(Fun, {Key, Value, Iter}) -> + case Fun(Key, Value) of + true -> {Key, Value}; + false -> do_search(Fun, maps:next(Iter)) + end. diff --git a/test/genlib_pmap_tests.erl b/test/genlib_pmap_tests.erl index 392d1ca..fc51aa7 100644 --- a/test/genlib_pmap_tests.erl +++ b/test/genlib_pmap_tests.erl @@ -127,7 +127,7 @@ no_leftovers_test() -> receive {worker, Pid} -> Pid end - || _ <- Ns + || _ <- Ns ], _ = exit(RunnerPid, enough), % lazy, i know diff --git a/test/prop_genlib_map.erl b/test/prop_genlib_map.erl index 6929444..e20b62e 100644 --- a/test/prop_genlib_map.erl +++ b/test/prop_genlib_map.erl @@ -6,11 +6,9 @@ prop_fold_while() -> ?FORALL( Map, - ?LET(KVList, non_empty(list({term(), term()})), maps:from_list(KVList)), + non_empty_map(), begin - RandomId = rand:uniform(map_size(Map)), - {RandomKey, RandomValue} = lists:nth(RandomId, maps:to_list(Map)), - + {RandomKey, RandomValue} = random_map_pair(Map), Result = genlib_map:fold_while( fun @@ -24,3 +22,47 @@ prop_fold_while() -> Result =:= RandomValue end ). + +-spec prop_search_found() -> proper:test(). +prop_search_found() -> + ?FORALL( + Map, + non_empty_map(), + begin + {RandomKey, RandomValue} = random_map_pair(Map), + Result = + genlib_map:search( + fun(K, _V) -> K =:= RandomKey end, + Map + ), + + {RandomKey, RandomValue} =:= Result + end + ). + +-spec prop_search_not_found() -> proper:test(). +prop_search_not_found() -> + ?FORALL( + Map, + map(), + begin + NonExistentKey = make_ref(), + Result = + genlib_map:search( + fun(K, _V) -> K =:= NonExistentKey end, + Map + ), + + false =:= Result + end + ). + +map() -> + ?LET(KVList, list({term(), term()}), maps:from_list(KVList)). + +non_empty_map() -> + ?LET(KVList, non_empty(list({term(), term()})), maps:from_list(KVList)). + +random_map_pair(Map) -> + RandomId = rand:uniform(map_size(Map)), + lists:nth(RandomId, maps:to_list(Map)).