Skip to content

Instantly share code, notes, and snippets.

@lucasdf
Last active April 4, 2017 16:02
Show Gist options
  • Save lucasdf/be9d13e8088857c1783f02be8d47c7c9 to your computer and use it in GitHub Desktop.
Save lucasdf/be9d13e8088857c1783f02be8d47c7c9 to your computer and use it in GitHub Desktop.
Exercises for Week 1 of the course Concurrent Programming in Erlang.
Concurrent Programming Erlang: https://www.futurelearn.com/courses/concurrent-programming-erlang
%% Based on code from
%% Erlang Programming
%% Francecso Cesarini and Simon Thompson
%% O'Reilly, 2008
%% http://oreilly.com/catalog/9780596518189/
%% http://www.erlangprogramming.org/
%% (c) Francesco Cesarini and Simon Thompson
-module(frequency).
-export([init/0, start_server/0]).
% Usage:
% 1> frequency:start_server().
% true
% 2> frequency ! {request, self(), allocate}.
% {request,<0.58.0>,allocate}
% 3> receive {reply, Reply} -> Reply end.
% {ok,10}
start_server() ->
register(?MODULE, spawn(?MODULE, init, [])).
%% These are the start functions used to create and
%% initialize the server.
init() ->
Frequencies = {get_frequencies(), []},
loop(Frequencies).
% Hard Coded
get_frequencies() -> [10,11,12,13,14,15].
%% The Main Loop
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
io:format("Allocate request~n"),
io:format("Frequencies list before processing: ~p~n", [Frequencies]),
{NewFrequencies, Reply} = allocate(Frequencies, Pid),
io:format("Frequencies list after processing: ~p~n~n", [NewFrequencies]),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid , {deallocate, Freq}} ->
io:format("Deallocate request: ~p~n", [Freq]),
io:format("Frequencies list before processing: ~p~n", [Frequencies]),
{NewFrequencies, Reply} = deallocate(Frequencies, Freq, Pid),
io:format("Frequencies list after processing: ~p~n~n", [NewFrequencies]),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid, stop} ->
Pid ! {reply, stopped}
end.
%% The Internal Help Functions used to allocate and
%% deallocate frequencies.
allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
case already_allocated(Allocated, Pid) of
true ->
{ {[Freq|Free], Allocated}, {error, already_allocated}};
false ->
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}
end.
deallocate({Free, Allocated}, Freq, Pid) ->
case pid_has_frequency(Allocated, Pid, Freq) of
true ->
NewAllocated=lists:keydelete(Freq, 1, Allocated),
{ {[Freq|Free], NewAllocated}, ok};
false ->
{ {Free, Allocated}, {error, not_allocated}}
end.
% already_allocated returns:
% true: Pid has been allocated a frequency
% false: Pid has not been allocated a frequency
% Maybe a better solution could be to have a separated list containing only Pid who have been allocated a frequency or to invert and use {Pid, Frequency} instead of {Frequency, Pid}.
already_allocated([], _Pid) ->
% If no frequency has been allocated (first parameter is an empty list) then this Pid has not been allocated any frequency.
false;
already_allocated(Allocated, Pid) ->
Pred = fun({_, APid}) -> APid =:= Pid end,
case lists:any(Pred, Allocated) of
true -> true;
false -> false
end.
% pid_has_frequency returns:
% true: Pid has been allocated the frequency Frequency
% false: Pid has not been allocated the frequency Frequency
pid_has_frequency(Allocated, Pid, Frequency) ->
Pred = fun({AFrequency, APid}) -> (APid =:= Pid) and (AFrequency =:= Frequency) end,
case lists:any(Pred, Allocated) of
true -> true;
false -> false
end.
%% Based on code from
%% Erlang Programming
%% Francecso Cesarini and Simon Thompson
%% O'Reilly, 2008
%% http://oreilly.com/catalog/9780596518189/
%% http://www.erlangprogramming.org/
%% (c) Francesco Cesarini and Simon Thompson
-module(frequency_assignment).
-export([start/0,allocate/0,deallocate/1,stop/0]).
-export([init/0]).
%% These are the start functions used to create and
%% initialize the server.
start() ->
register(frequency_assignment,
spawn(frequency_assignment, init, [])).
init() ->
Frequencies = {get_frequencies(), []},
loop(Frequencies).
% Hard Coded
get_frequencies() -> [10,11,12,13,14,15].
%% The Main Loop
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
timer:sleep(5200),
{NewFrequencies, Reply} = allocate(Frequencies, Pid),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid , {deallocate, Freq}} ->
timer:sleep(5200),
NewFrequencies = deallocate(Frequencies, Freq),
Pid ! {reply, ok},
loop(NewFrequencies);
{request, Pid, stop} ->
timer:sleep(5200),
Pid ! {reply, stopped}
end.
%% Functional interface
allocate() ->
clear(),
frequency_assignment ! {request, self(), allocate},
receive
{reply, Reply} -> Reply
after 5000 ->
timeout
end.
deallocate(Freq) ->
clear(),
frequency_assignment ! {request, self(), {deallocate, Freq}},
receive
{reply, Reply} -> Reply
after 5000 ->
timeout
end.
stop() ->
clear(),
frequency_assignment ! {request, self(), stop},
receive
{reply, Reply} -> Reply
after 5000 ->
timeout
end.
clear() ->
receive
Msg ->
io:format("Clearing message ~p~n~n",[Msg]),
clear()
after 0 ->
timeout
end.
%% The Internal Help Functions used to allocate and
%% deallocate frequencies.
allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}.
deallocate({Free, Allocated}, Freq) ->
NewAllocated=lists:keydelete(Freq, 1, Allocated),
{[Freq|Free], NewAllocated}.
-module(frequency_test).
-export([all/0]).
-define(TESTED_MODULE, frequency).
% Usage:
% 1> frequency_test:all().
all() ->
% start the test named process.
start_test_server(),
% first request to allocate. Should be allocated the frequency 10.
test_allocate({reply, {ok, 10}}),
% Trying to allocated another frequency. Should receive 'already_allocated' error
test_allocate({reply,{error,already_allocated}}),
% Trying to deallocate frequency no allocated to this client. Should receive 'not_allocated' error.
test_deallocate({reply,{error,not_allocated}}, 11),
% Trying to deallocate the frequency allocated before. Should succeed.
test_deallocate({reply, ok}, 10),
% tests are over, stop the named process.
stop_test_server(),
tests_success.
% test the allocate function on 'frequency' process
test_allocate(ExpectedAnswer) ->
?TESTED_MODULE ! {request, self(), allocate},
receive
ExpectedAnswer -> true;
Any ->
io:format("Expected ~p ~n", [ExpectedAnswer]),
io:format("Got ~p instead ~n", [Any]),
exit(tests_not_success)
end.
% test the deallocate function on 'frequency' process
test_deallocate(ExpectedAnswer, Freq) ->
?TESTED_MODULE ! {request, self(), {deallocate, Freq}},
receive
ExpectedAnswer -> true;
Any ->
io:format("Expected ~p ~n", [ExpectedAnswer]),
io:format("Got ~p instead ~n", [Any]),
exit(tests_not_success)
end.
% start_test_server will start the frequency server for the tests. We check if the 'frequency' process already exists and if it does we kill it and then start a new one. We want to work on a fresh new process to guarantee the tests integrity.
start_test_server() ->
case whereis(?TESTED_MODULE) of
undefined ->
% The named process 'frequency' does not exist. Just start a new one.
?TESTED_MODULE:start_server();
Pid ->
% The named process 'frequency' already exists. Send stop message, unregister it and then start it again.
unregister(?TESTED_MODULE),
Pid ! {request, self(), stop},
receive
{reply, stopped} -> true
end,
?TESTED_MODULE:start_server()
end.
% stop_test_server will stop the testing server.
stop_test_server() ->
case whereis(?TESTED_MODULE) of
undefined -> true;
Pid ->
% The named process 'frequency' already exists. Send stop message, unregister it and then start it again.
unregister(?TESTED_MODULE),
Pid ! {request, self(), stop},
receive
{reply, stopped} -> true
end
end.
-module(receiver).
-export([receiver/0, receiver2/0]).
% Usage example:
% Server = spawn(ordered, receiver, []).
% 61> Server ! second.
% second % The message has not been processed because it is waiting for a message containing atom 'first'
% 62> Server ! first.
% Processed: first
% Processed: second
% Another example:
% Server = spawn(ordered, receiver, []).
% 1> Server ! first.
% Processed: first
% 2> Server ! first.
% The last message won't be processed because the process is waiting for a message containing the atom 'second'
receiver() ->
receive
first ->
io:format("Processed: first~n")
end,
receive
second ->
io:format("Processed: second~n")
end,
receiver().
-module(palin).
-export([palin/1,nopunct/1,palindrome/1, server/0, server/1]).
% BEGINNING OF EXERCISE
% Simple Server Usage:
% 4> Server = spawn(palin, server, [self()]).
% <0.71.0>
% 5> Server ! {check, "Was it a cat I saw"}.
% {check,"Was it a cat I saw"}
% 6> flush().
% Shell got {result,"Was it a cat I saw is a palindrome."}
% ok
server(Pid) ->
receive
{check, Msg} ->
case palindrome(Msg) of
true ->
Pid ! {result, Msg ++ " is a palindrome."};
false ->
Pid ! {result, Msg ++ " is not a palindrome."}
end,
server(Pid)
end.
% END OF EXERCISE
% palindrome problem
%
% palindrome("Madam I\'m Adam.") = true
palindrome(Xs) ->
palin(nocaps(nopunct(Xs))).
nopunct([]) ->
[];
nopunct([X|Xs]) ->
case lists:member(X,".,\ ;:\t\n\'\"") of
true ->
nopunct(Xs);
false ->
[ X | nopunct(Xs) ]
end.
nocaps([]) ->
[];
nocaps([X|Xs]) ->
[ nocap(X) | nocaps(Xs) ].
nocap(X) ->
case $A =< X andalso X =< $Z of
true ->
X+32;
false ->
X
end.
% literal palindrome
palin(Xs) ->
Xs == reverse(Xs).
reverse(Xs) ->
shunt(Xs,[]).
shunt([],Ys) ->
Ys;
shunt([X|Xs],Ys) ->
shunt(Xs,[X|Ys]).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment