Просмотр исходного кода

feat(wdgraph): add `fold/3` which folds over graph edges

Andrew Mayorov 3 лет назад
Родитель
Сommit
0d39546080
2 измененных файлов с 43 добавлено и 1 удалено
  1. 23 1
      apps/emqx/src/emqx_wdgraph.erl
  2. 20 0
      apps/emqx/test/emqx_wdgraph_tests.erl

+ 23 - 1
apps/emqx/src/emqx_wdgraph.erl

@@ -27,6 +27,8 @@
 -export([find_edge/3]).
 -export([get_edges/2]).
 
+-export([fold/3]).
+
 -export([find_shortest_path/3]).
 
 -export_type([t/0]).
@@ -38,7 +40,7 @@
 -type label() :: term().
 
 -opaque t() :: t(gnode(), label()).
--opaque t(Node, Label) :: gb_trees:tree({Node}, {Node, weight(), Label}).
+-opaque t(Node, Label) :: gb_trees:tree({Node}, [{Node, weight(), Label}]).
 
 %%
 
@@ -72,6 +74,26 @@ find_edge(From, To, G) ->
 get_edges(Node, G) ->
     tree_lookup({Node}, G, []).
 
+-spec fold(FoldFun, Acc, t(Node, Label)) -> Acc when
+    FoldFun :: fun((Node, _Edge :: {Node, weight(), Label}, Acc) -> Acc).
+fold(FoldFun, Acc, G) ->
+    fold_iterator(FoldFun, Acc, gb_trees:iterator(G)).
+
+fold_iterator(FoldFun, AccIn, It) ->
+    case gb_trees:next(It) of
+        {{Node}, Edges = [_ | _], ItNext} ->
+            AccNext = lists:foldl(
+                fun(Edge = {_To, _Weight, _Label}, Acc) ->
+                    FoldFun(Node, Edge, Acc)
+                end,
+                AccIn,
+                Edges
+            ),
+            fold_iterator(FoldFun, AccNext, ItNext);
+        none ->
+            AccIn
+    end.
+
 % Find the shortest path between two nodes, if any. If the path exists, return list
 % of edge labels along that path.
 % This is a Dijkstra shortest path algorithm. It is one-way right now, for

+ 20 - 0
apps/emqx/test/emqx_wdgraph_tests.erl

@@ -40,6 +40,26 @@ edges_nodes_test_() ->
         ?_assertEqual([{baz, 1, "cheapest"}, {foo, 0, "free"}], emqx_wdgraph:get_edges(bar, G5))
     ].
 
+fold_test_() ->
+    G1 = emqx_wdgraph:new(),
+    G2 = emqx_wdgraph:insert_edge(foo, bar, 42, "fancy", G1),
+    G3 = emqx_wdgraph:insert_edge(bar, baz, 1, "cheapest", G2),
+    G4 = emqx_wdgraph:insert_edge(bar, foo, 0, "free", G3),
+    G5 = emqx_wdgraph:insert_edge(foo, bar, 100, "luxury", G4),
+    [
+        ?_assertEqual(
+            % 100 + 0 + 1
+            101,
+            emqx_wdgraph:fold(fun(_From, {_, Weight, _}, Acc) -> Weight + Acc end, 0, G5)
+        ),
+        ?_assertEqual(
+            [bar, baz, foo],
+            lists:usort(
+                emqx_wdgraph:fold(fun(From, {To, _, _}, Acc) -> [From, To | Acc] end, [], G5)
+            )
+        )
+    ].
+
 nonexistent_nodes_path_test_() ->
     G1 = emqx_wdgraph:new(),
     G2 = emqx_wdgraph:insert_edge(foo, bar, 42, "fancy", G1),