flow_graph.erl 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. -module(flow_graph).
  2. -include("../include/flow_graph.hrl").
  3. %%% Operation construction
  4. -export([func/1, value/1, values/1, dynamic/1, route/2,
  5. pipe/1, parallel/1, sequence/1, funnel/1,
  6. split/1, merge/1, switch/2, loop/2]).
  7. %%% Operation querying
  8. -export([in_arity/1, out_arity/1, can_connect/2]).
  9. %%%-----------------------------------------------------------------------------
  10. %%% Operation construction
  11. %%%-----------------------------------------------------------------------------
  12. func(F) when is_function(F) ->
  13. {arity, N} = erlang:fun_info(F, arity),
  14. #func{in=N, code=F}.
  15. value(X) -> #value{value=X}.
  16. values(Xs = [_|_]) ->
  17. parallel(lists:map(fun value/1, Xs)).
  18. dynamic(Gen) when is_function(Gen) ->
  19. {arity, N} = erlang:fun_info(Gen, arity),
  20. #dynamic{in=N, code=Gen}.
  21. route(N, Map) ->
  22. try
  23. lists:foldl(fun(Target, Count) when Target =< N -> Count+1;
  24. (Target, _Count) when Target > N -> throw(out_of_bounds)
  25. end,
  26. 0, Map)
  27. of
  28. Count -> #route{in=N, out=Count, map=Map}
  29. catch
  30. throw:out_of_bounds -> erlang:error(badarg, [N, Map])
  31. end.
  32. pipe(Ops = [First|Rest]) ->
  33. try
  34. lists:foldl(fun(Op2, Op1) ->
  35. case can_connect(Op1, Op2) of
  36. true -> Op2;
  37. false -> throw(bad_connection)
  38. end
  39. end,
  40. First, Rest)
  41. of
  42. Last -> #pipe{in=in_arity(First), out=out_arity(Last), ops=Ops}
  43. catch
  44. throw:bad_connection -> erlang:error(badarg, [Ops])
  45. end.
  46. parallel(Ops = [_|_]) ->
  47. {In, Out} = flatten_arity(Ops),
  48. #parallel{in=In, out=Out, ops=Ops}.
  49. sequence(Ops = [_|_]) ->
  50. {In, Out} = flatten_arity(Ops),
  51. #sequence{in=In, out=Out, ops=Ops}.
  52. split(N) -> #split{size=N}.
  53. merge(N) -> #merge{size=N}.
  54. funnel(N) -> #funnel{size=N}.
  55. switch(Switch, Map = [{_, Op1}|Rest]) ->
  56. try
  57. {I, O} = lists:foldl(
  58. fun({_, Op}, {In, Out}) -> known_op_arity(Op, In, Out) end,
  59. {in_arity(Op1), out_arity(Op1)}, Rest),
  60. known_op_arity(Switch, I, 1),
  61. {I, O}
  62. of
  63. {In, Out} -> #switch{in=In, out=Out, switch=Switch, map=Map}
  64. catch
  65. error:function_clause -> erlang:error(badarg, [Switch, Map])
  66. end.
  67. loop(Init, Op) ->
  68. S = out_arity(Init),
  69. {In, Out} = case {in_arity(Op)-S, out_arity(Op)-S} of
  70. {I, O} when I >= 0, O > 0 -> {I+in_arity(Init), O};
  71. {_, _} -> erlang:error(badarg, [Init, Op])
  72. end,
  73. #loop{in=In, out=Out, init=Init, op=Op}.
  74. %%%-----------------------------------------------------------------------------
  75. %%% Operation querying
  76. %%%-----------------------------------------------------------------------------
  77. in_arity(#func{in=In}) -> In;
  78. in_arity(#value{}) -> 0;
  79. in_arity(#dynamic{in=In}) -> In;
  80. in_arity(#route{in=In}) -> In;
  81. in_arity(#pipe{in=In}) -> In;
  82. in_arity(#parallel{in=In}) -> In;
  83. in_arity(#sequence{in=In}) -> In;
  84. in_arity(#split{}) -> 1;
  85. in_arity(#merge{size=N}) -> N;
  86. in_arity(#funnel{size=N}) -> N;
  87. in_arity(#switch{in=In}) -> In;
  88. in_arity(#loop{in=In}) -> In.
  89. out_arity(#func{}) -> 1;
  90. out_arity(#value{}) -> 1;
  91. out_arity(#dynamic{}) -> unknown;
  92. out_arity(#route{out=Out}) -> Out;
  93. out_arity(#pipe{out=Out}) -> Out;
  94. out_arity(#parallel{out=Out}) -> Out;
  95. out_arity(#sequence{out=Out}) -> Out;
  96. out_arity(#split{size=N}) -> N;
  97. out_arity(#merge{}) -> 1;
  98. out_arity(#funnel{}) -> 1;
  99. out_arity(#switch{out=Out}) -> Out;
  100. out_arity(#loop{out=Out}) -> Out.
  101. flatten_arity([First|Rest]) ->
  102. lists:foldl(fun(Op, {In, Out}) ->
  103. {add_arity(In, in_arity(Op)),
  104. add_arity(Out, out_arity(Op))}
  105. end,
  106. {in_arity(First), out_arity(First)}, Rest).
  107. add_arity(unknown, _) -> unknown;
  108. add_arity(_, unknown) -> unknown;
  109. add_arity(N, M) -> N + M.
  110. known_arity(N, N) -> N;
  111. known_arity(unknown, N) -> N;
  112. known_arity(N, unknown) -> N.
  113. known_op_arity(Op, In, Out) ->
  114. {known_arity(in_arity(Op), In), known_arity(out_arity(Op), Out)}.
  115. can_connect(Op1, Op2) ->
  116. case {out_arity(Op1), in_arity(Op2)} of
  117. {N, N} -> true;
  118. {unknown, _} -> true;
  119. _ -> false
  120. end.