Skip to content

Instantly share code, notes, and snippets.

@elbrujohalcon
Last active July 6, 2020 21:15
Show Gist options
  • Save elbrujohalcon/8d3366fe1765c63a3e48d6d5589f1391 to your computer and use it in GitHub Desktop.
Save elbrujohalcon/8d3366fe1765c63a3e48d6d5589f1391 to your computer and use it in GitHub Desktop.
Palindrome
-module(palin).
-export([test/0]).
-export([server/0]).
-export([start/0, check/2, stop/1]).
test() ->
Checks = [{"Abba", true},
{"baba", false},
{"Yo hago yoga hoy", true},
{"Yo haré yoga mañana", false},
{"Acaso hubo buhos aca", true},
{"Acaso habra buhos aqui", false},
{"", true},
{"...", true}],
Server = palin:start(),
ok = lists:foreach(fun ({I, _}) ->
start_test_client(self(), Server, I)
end,
Checks),
ok = lists:foreach(fun ({I, O}) ->
{I, O} = {I, check_test_client(I)}
end,
Checks),
stop = palin:stop(Server),
ok.
start_test_client(Caller, Server, Input) ->
spawn(fun () ->
Output = palin:check(Server, Input),
Caller ! {Input, Output}
end).
check_test_client(Input) ->
receive
{Input, Output} ->
Output
end.
%% API -------------------------------------------------------------------------
start() ->
spawn(palin, server, []).
check(Server, String) ->
Server ! {check, self(), String},
receive
X ->
X
end.
stop(Server) ->
Server ! stop.
%% SERVER ----------------------------------------------------------------------
server() ->
receive
{check, Caller, String} ->
Caller ! pal_check(String),
server();
stop ->
ok
end.
%% SERVER INTERNALS ------------------------------------------------------------
rem_punct(String) ->
lists:filter(fun (Ch) ->
not lists:member(Ch, "\"'\t\n ")
end,
String).
to_small(String) ->
lists:map(fun (Ch) ->
case $A =< Ch andalso Ch =< $Z of
true ->
Ch + 32;
false ->
Ch
end
end,
String).
pal_check(String) ->
Normalise = to_small(rem_punct(String)),
lists:reverse(Normalise) == Normalise.
-module(palin).
%% Tests
-export([test/0]).
%% Server API
-export([server/0, server/1]).
%% Client API
-export([start/1, check/2, stop/1]).
%% @doc Runs the tests
test() ->
Checks = [{"Abba", true},
{"baba", false},
{"Yo hago yoga hoy", true},
{"Yo haré yoga mañana", false},
{"Acaso hubo buhos aca", true},
{"Acaso habra buhos aqui", false},
{"", true},
{"...", true}],
Server = palin:start(3),
ok = lists:foreach(fun ({I, _}) ->
start_test_client(self(), Server, I)
end,
Checks),
ok = lists:foreach(fun ({I, O}) ->
{I, O} = {I, check_test_client(I)}
end,
Checks),
stop = palin:stop(Server),
ok.
start_test_client(Caller, Server, Input) ->
spawn(fun () ->
Output = palin:check(Server, Input),
Caller ! {Input, Output}
end).
check_test_client(Input) ->
receive
{Input, Output} ->
Output
end.
%% API -------------------------------------------------------------------------
%% @doc Boots up the system with N servers to handle requests.
%% Returns the Pid of the broker that users should use for other API calls.
-spec start(pos_integer()) -> pid().
start(Servers) ->
spawn(palin, server, [Servers]).
%% @doc Boots up the system with N servers to handle requests.
%% Sends a request for palindrome-checking to the broker.
%% The request will then be processed by a server and the answer will be
%% sent back to this client process.
%% That answer is then returned.
-spec check(pid(), string()) -> boolean().
check(Server, String) ->
% Sent a request to the server, including the client Pid so the server knows
% where to send the response
Server ! {check, self(), String},
% Wait for a response from the server and
receive
X ->
X % use it as the result of this function
end.
%% @doc Sends the stop signal to the broker, therefore stopping the whole system
-spec stop(pid()) -> stop.
stop(Server) ->
Server ! stop.
%% SERVER ----------------------------------------------------------------------
%% @doc Starts the broker, which is a process that will loop until it gets a
%% 'stop' message, distributing the requests in a round-robin fashion
%% among the ServerCount servers.
-spec server(pos_integer()) -> ok.
server(ServerCount) ->
% Start each of the servers
Servers = [spawn(palin, server, []) || _ <- lists:seq(1, ServerCount)],
% Evaluate the recursive broker/1 function to receive messages
broker(Servers).
%% @doc The broker loop. It loops until it gets a 'stop' message.
%% Each {check, pid(), string()} message that it receives is delivered to
%% the first available server.
%% The return type of this function is 'ok' since it terminates after
%% evaluating lists:foreach/2.
-spec broker([pid()]) -> ok.
broker([Server | OtherServers] = Servers) ->
receive
{check, Caller, String} ->
% check command is sent to the first server
Server ! {check, Caller, String},
% we don't wait for a response here, just update the state and keep looping
broker(OtherServers ++ [Server]);
stop ->
% turn off all the servers and return
lists:foreach(fun (S) ->
S ! stop
end,
Servers)
end.
%% @doc Loops until it gets a 'stop' message from the broker.
%% Each {check, pid(), string()} message that it receives is processed and
%% the result is sent to the caller.
%% The return type of this function is 'ok' since it terminates receiving
%% stop.
-spec server() -> ok.
server() ->
receive
{check, Caller, String} ->
Result = pal_check(String),
% instrumentation to verify that everything is working as expected
io:format("~p checked ~s for ~p: ~p~n", [self(), String, Caller, Result]),
% the result is sent back to the original caller
Caller ! Result,
% the server keeps looping
server();
stop ->
ok
end.
%% SERVER INTERNALS ------------------------------------------------------------
rem_punct(String) ->
lists:filter(fun (Ch) ->
not lists:member(Ch, "\"'\t\n ")
end,
String).
to_small(String) ->
lists:map(fun (Ch) ->
case $A =< Ch andalso Ch =< $Z of
true ->
Ch + 32;
false ->
Ch
end
end,
String).
pal_check(String) ->
Normalise = to_small(rem_punct(String)),
lists:reverse(Normalise) == Normalise.
-module(palin).
-export([test/0]).
-export([server/1]).
-export([start/0, check/2, stop/1]).
test() ->
Server = palin:start(),
true = palin:check(Server, "Abba"),
false = palin:check(Server, "baba"),
stop = palin:stop(Server),
ok.
%% API -------------------------------------------------------------------------
start() ->
spawn(palin, server, [self()]).
check(Server, String) ->
Server ! {check, String},
receive X -> X end.
stop(Server) ->
Server ! stop.
%% SERVER ----------------------------------------------------------------------
server(Caller) ->
receive
{check, String} ->
Caller ! pal_check(String),
server(Caller);
stop ->
ok
end.
%% SERVER INTERNALS ------------------------------------------------------------
rem_punct(String) ->
lists:filter(fun (Ch) ->
not lists:member(Ch, "\"'\t\n ")
end,
String).
to_small(String) ->
lists:map(fun (Ch) ->
case $A =< Ch andalso Ch =< $Z of
true ->
Ch + 32;
false ->
Ch
end
end,
String).
pal_check(String) ->
Normalise = to_small(rem_punct(String)),
lists:reverse(Normalise) == Normalise.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment