2 - erlang exe

Parallel Map

Write a function pmap/2 that applies a given function to each element of a list in parallel.

Example:

pmap(fun (X) -> X*X end, [1,2,3,4])
% [1,4,9,16]

Solution:

pmap(F, L) ->
    Ps = [spawn(?MODULE, execute, [F, X, self()]) || X <- L],
    [receive 
         {Pid, X} -> X
     end || Pid <- Ps].

execute(F, X, Pid) ->
	Pid ! {self(), F(X)}.

Parallel Filter

Write a function pfilter/2 that filters a list based on a given predicate in parallel.

Example:

pfilter(fun (X) -> X > 2 end, [1,2,3,4])
% [3,4]

Solution:

pfilter(P, L) ->
  Ps = [spawn(?MODULE, check, [P, X, self()]) || X <- L],
  lists:foldl(fun (Pid, Vo) ->
                      receive
                          {Pid, true, X} -> Vo ++ [X];
                          {Pid, false, _} -> Vo
                      end
             end, [], Ps).

check(P, X, Pid) ->
  Pid ! {self(), P(X), X}.

Partition List

Write a function partition/2 that partitions a list into N chunks.

Example:

partition([1,2,3,4,5,6,7,8,9,10], 3)
% [[1,2,3],[4,5,6],[7,8,9,10]]

Solution:

partition(L, N) -> 
    M = length(L),
    Chunk = M div N,
    End = M - Chunk*(N-1),
    parthelp(L, N, 1, Chunk, End, []).

parthelp(L, 1, P, _, E, Res) ->
    Res ++ [lists:sublist(L, P, E)];
parthelp(L, N, P, C, E, Res) ->
    R = lists:sublist(L, P, C),
    parthelp(L, N-1, P+C, C, E, Res ++ [R]).

Parallel Fold

Write a function parfold/3 that folds a list in parallel using a given function.

Example:

parfold(fun (X, Y) -> X + Y end, [1,2,3,4,5,6,7,8,9,10], 3)
% 55

Solution:

%% From previous exercise %%
partition(L, N) -> 
    M = length(L),
    Chunk = M div N,
    End = M - Chunk*(N-1),
    parthelp(L, N, 1, Chunk, End, []).

parthelp(L, 1, P, _, E, Res) ->
    Res ++ [lists:sublist(L, P, E)];
parthelp(L, N, P, C, E, Res) ->
    R = lists:sublist(L, P, C),
    parthelp(L, N-1, P+C, C, E, Res ++ [R]).
%% % %%

parfold(F, L, N) ->
    Ls = partition(L, N),
    Ps = [spawn(?MODULE, dofold, [self(), F, X]) || X <- Ls],
    [R|Rs] = [receive
                  {P, V} -> V 
              end || P <- Ps],
    lists:foldl(F, R, Rs).

dofold(Pid, F, [X|Xs]) ->
    Pid ! {self(), lists:foldl(F, X, Xs)}.

Process Linking and Monitoring

Write a function master/1 that spawns a list of functions as linked processes and monitors their execution.

Example:

master([fun () -> timer:sleep(1000), io:fwrite("Hello, World!\n") end])
% "Hello, World!\n"
% ok

Solution:

listlink([], Pids) -> Pids;
listlink([F|Fs], Pids) ->
    Pid = spawn_link(F),
    listlink(Fs, Pids#{Pid => F}).

master(Functions) ->
    process_flag(trap_exit, true),
    Workers = listlink(Functions, #{}),
    master_loop(Workers, length(Functions)).

master_loop(_, 0) ->
    ok; 
master_loop(Workers, Count) ->
    receive
        {'EXIT', _, normal} ->
            master_loop(Workers, Count-1);
        {'EXIT', Pid, _} ->
            #{Pid := Fun} = Workers,
            Child = spawn_link(Fun),
            master_loop(Workers#{Child => Fun}, Count)
    end.