views:

59

answers:

0

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.