I am making my way through Joe Armstrong's book on programing Erlang. I came up with the following answer for the ring benchmark question. The code works but I am not sure if it is "Erlangic" (for lack of a better word). In particular I am not sure if I am using too many Guards.
Can more experienced programmers critique the code? I had posted the same request in my blog a while back but hardly anyone reads it :P
Let me know if this question should be a community wiki; I'll gladly mark it as such.
-module(pring).
%%
%% Exported Functions
%%
-export([run/2]).
-ifdef(debug).
-define(DEBUG(Format, Args), io:format(Format, Args)).
-else.
-define(DEBUG(Format, Args), void).
-endif.
%%
%% API Functions
%%
run(N, M) when is_integer(N), is_integer(M), N > 0, M > 0 ->
time_it(fun() -> start(N, M) end).
%%
%% Local Functions
%%
time_it(F) when is_function(F) ->
statistics(runtime),
statistics(wall_clock),
Result = F(),
{_, CPUTime} = statistics(runtime),
{_, ElapsedTime} = statistics(wall_clock),
{Result, CPUTime/1000, ElapsedTime/1000}.
start(N, M) when is_integer(N), is_integer(M), N > 0, M > 0 ->
This = self(),
First = spawn(fun() -> loop(This, This) end),
?DEBUG("Creating ~p for N = ~p~n", [First, N]),
Last = create(First, First, N - 1),
First ! {realign, First, Last},
First ! {relay, message, M, This},
receive
{done, message} ->
N * M
end.
create(First, Previous, N) when is_pid(First), is_pid(Previous), is_integer(N), N > 0 ->
Pid = spawn(fun() -> loop(First, Previous) end),
?DEBUG("Creating ~p for N = ~p~n", [Pid, N]),
create(First, Pid, N - 1);
create(First, Previous, 0) when is_pid(First), is_pid(Previous) ->
Previous.
send_relay_message(Target, Message, M, ReportBack) when is_pid(Target), is_integer(M), is_pid(ReportBack) ->
?DEBUG("~p => ~p (M = ~p) ~n", [self(), Target, M + 1]),
Target ! {relay, Message, M, ReportBack}.
loop(First, Previous) when is_pid(First), is_pid(Previous) ->
This = self(),
receive
{realign, NewFirst, NewPrevious} when is_pid(NewFirst), is_pid(NewPrevious) ->
?DEBUG("I am ~p Realigning: First ~p to ~p, Previous ~p to ~p~n", [This, First, NewFirst, Previous, NewPrevious]),
loop(NewFirst, NewPrevious);
{relay, Message, M, ReportBack} when is_integer(M), is_pid(ReportBack), This =:= First, M > 0 ->
send_relay_message(Previous, Message, M - 1, ReportBack),
loop(First, Previous);
{relay, Message, M, ReportBack} when is_integer(M), is_pid(ReportBack), This =:= First, M =:= 0 ->
?DEBUG("I am ~p All rounds of relay over~n", [This]),
ReportBack ! {done, Message},
void;
{relay, Message, M, ReportBack} when is_integer(M), is_pid(ReportBack), This =/= First, M > 0 ->
send_relay_message(Previous, Message, M, ReportBack),
loop(First, Previous);
{relay, Message, M, ReportBack} when is_integer(M), is_pid(ReportBack), This =/= First, M =:= 0 ->
send_relay_message(Previous, Message, M, ReportBack),
void;
_Other ->
?DEBUG("I am ~p I don't understand ~p~n", [This, _Other]),
loop(First, Previous)
end.
If you have any problems understanding my intention/approach please ask.