Solutions to Erlang exercises

General syntax

reverse([]) -> [];
reverse([X|Xs]) -> reverse(Xs) ++ [X].
find(_, []) -> not_found;
find(X, [X|_]) -> {found, X};
find(X, [_|Ys]) -> find(X, Ys).
delete(_, []) -> [];
delete(X, [X|Ys]) -> Ys;
delete(X, [Y|Ys]) -> [Y|delete(X, Ys)].
flatten([]) -> [];
flatten([Xs|Ys]) -> Xs ++ flatten(Ys).
square1([]) -> [];
square1([X|Xs]) -> [X*X | square1(Xs)].

square2(Xs) -> [X*X || X <- Xs].

square3(Xs) -> lists:map(fun(X) -> X*X end, Xs).
filter1(_, []) -> [];
filter1(F, [X|Xs]) ->
  case F(X) of
    true -> [X|filter1(F, Xs)] ;
    false -> filter1(F, Xs)
  end.

filter2(_, []) -> [];
filter2(F, [X|Xs]) ->
  Result = F(X),
  if
    Result -> [X|filter2(F, Xs)] ;
    true   -> filter2(F, Xs)
  end.

filter3(F, Xs) -> [X || X <- Xs, F(X)].

Simple message passing

avg_server1(Avg) ->
  io:fwrite("Current average is ~p~n", [Avg]),
  receive
    Num -> avg_server1((Avg + Num) / 2)
  end.
avg_server2() ->
  receive
    Num -> avg_server2([Num])
  end.
avg_server2(Nums) ->
  io:fwrite("The average of ~p is ~p~n", [Nums, sum(Nums) / length(Nums)]),
  receive
    Num -> avg_server2(Nums ++ [Num])
  end.
sum([]) -> 0;
sum([X|Xs]) -> X + sum(Xs).
% Main server function
queue_server(Q) ->
  receive
    {Pid, push, Val} -> Pid ! ok, queue_server(Q ++ [Val]) ;
    {Pid, pop} when length(Q) > 0 -> Pid ! hd(Q), queue_server(tl(Q))
  end.

% API
new_queue() -> spawn(fun() -> queue_server([]) end).
push(Pid, Val) ->
  Pid ! {self(), push, Val},
  receive ok -> ok end.
pop(Pid) ->
  Pid ! {self(), pop},
  receive Val -> Val end.

Semaphores in Erlang

Q1

-module(sem).
-export([createSem/1, acquire/1, release/1]).

body(Val) ->
  receive
    {acquire, Pid} when Val > 0 ->
      Pid ! ok,
      body(Val - 1) ;
    release ->
      body(Val + 1)
  end.

createSem(InitialValue) ->
  spawn(fun() -> body(InitialValue) end).

acquire(Semaphore) ->
  Semaphore ! {acquire, self()},
  receive
    ok -> ok
  end.

release(Semaphore) ->
  Semaphore ! release,
  ok.

Q2

-module(semtester).
-export([test/0]).

testN(N, Sem) ->
  sem:acquire(Sem),
  io:fwrite("~p in~n", [N]),
  timer:sleep(500),
  io:fwrite("~p out~n", [N]),
  sem:release(Sem),
  testN(N, Sem).

test() ->
  Sem = sem:createSem(1),
  lists:foreach(
    fun(N) -> spawn(fun() -> testN(N, Sem) end) end,
    lists:seq(1,5)
  ).

Elevator

Q1

New function loop/1 needs to be defined, while the definition of loop/0 needs to be changed to call loop/1.

loop() -> loop([]).

loop(Buffer) when length(Buffer) >= 10 ->
  net_io:transmit(Buffer),
  loop([]);
loop(Buffer) ->
  receive
    {request, Pid, Ref, {send, Msg}} ->
      Pid ! {result, Ref, ok},
      loop(Buffer ++ [Msg])
  end.

Q2

The definitions of the loop/0 and loop/1 should be replaced with the following definitions of loop/0 and loop/2.

loop() ->
  receive
    {request, Pid, Ref, {send, Msg}} ->
      Pid ! {result, Ref, ok},
      Self = self(),
      RefT = make_ref(),
      spawn(fun () ->
        receive after 100 -> Self!{timeout, RefT} end
      end),
      loop([Msg], RefT)
  end.

loop(Buffer, _) when length(Buffer) >= 10 ->
  transmit(Buffer),
  loop();
loop(Buffer, RefT) ->
  receive
    {request, Pid, Ref, {send, Msg}} ->
      Pid ! {result, Ref, ok},
      loop(Buffer ++ [Msg], RefT);
    {timeout, RefT} ->
      transmit(Buffer),
      loop()
  end.

Here make_ref() is used to make sure that we do not receive old timeout messages. This might be handled in some other way, but it is required to address this problem.



Concurrent Programming 2016 - Chalmers University of Technology & Gothenburg University