/src/support/z_toposort.erl

http://github.com/zotonic/zotonic · Erlang · 81 lines · 51 code · 11 blank · 19 comment · 5 complexity · c4d60768fafc22a977b6f920fd0fea21 MD5 · raw file

  1. %% @author Marc Worrell <marc@worrell.nl>
  2. %% @copyright 2011 Marc Worrell
  3. %% @doc Simple topological sort of tuples {item, [depends], [provides]}
  4. %% Copyright 2011 Marc Worrell
  5. %%
  6. %% Licensed under the Apache License, Version 2.0 (the "License");
  7. %% you may not use this file except in compliance with the License.
  8. %% You may obtain a copy of the License at
  9. %%
  10. %% http://www.apache.org/licenses/LICENSE-2.0
  11. %%
  12. %% Unless required by applicable law or agreed to in writing, software
  13. %% distributed under the License is distributed on an "AS IS" BASIS,
  14. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. %% See the License for the specific language governing permissions and
  16. %% limitations under the License.
  17. -module(z_toposort).
  18. -export([
  19. sort/1
  20. ]).
  21. %% @doc Return the topological sort of a list.
  22. -type topoitems() :: {Name::term(), Depends::list(), Provides::list()}.
  23. -spec sort(topoitems()) -> {ok, list()} | {error, {cyclic, list()}}.
  24. sort([]) ->
  25. {ok, []};
  26. sort(L) ->
  27. G = digraph:new(),
  28. Vs = [ {N, digraph:add_vertex(G)} || {N, _, _} <- L ],
  29. add_node(G, L, L, Vs).
  30. add_node(G, _Nodes, [], Vs) ->
  31. case digraph_utils:is_acyclic(G) of
  32. true ->
  33. SortedVs = digraph_utils:topsort(G),
  34. digraph:delete(G),
  35. {ok, vertices_to_nodes(SortedVs, Vs)};
  36. false ->
  37. Cycles = digraph_utils:cyclic_strong_components(G),
  38. digraph:delete(G),
  39. {error, {cyclic, [ vertices_to_nodes(Components, Vs) || Components <- Cycles ]}}
  40. end;
  41. add_node(G, Nodes, [{_N, [], _Provides}|L], Vs) ->
  42. add_node(G, Nodes, L, Vs);
  43. add_node(G, Nodes, [{Node, Depends, _Provides}|L], Vs) ->
  44. {Node, NVx} = proplists:lookup(Node, Vs),
  45. DepNodes = lists:flatten([ find_node(Nodes, [], Depend) || Depend <- Depends ]),
  46. [
  47. begin
  48. {N, Vx} = proplists:lookup(N, Vs),
  49. digraph:add_edge(G, Vx, NVx)
  50. end
  51. || N <- DepNodes
  52. ],
  53. add_node(G, Nodes, L, Vs).
  54. % find_node([], [], D) ->
  55. % throw({error, {missing_provide, D}});
  56. find_node([], Fs, _D) ->
  57. Fs;
  58. find_node([{N, _, Provides}|L], Fs, D) ->
  59. case lists:member(D, Provides) of
  60. true -> find_node(L, [N|Fs], D);
  61. false -> find_node(L, Fs, D)
  62. end.
  63. vertices_to_nodes(Vertices, Nodes) ->
  64. [
  65. begin
  66. {value, {N,_}} = lists:keysearch(V, 2, Nodes),
  67. N
  68. end
  69. || V <- Vertices
  70. ].