Solutions
Solutions
2 Sequential Programming 4
2.1 Evaluating Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Creating Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Side Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 Database Handling Using Lists . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.5 ADVANCED: Manipulating Lists . . . . . . . . . . . . . . . . . . . . . . . . 6
2.6 ADVANCED: Implement Quicksort and Merge Sort . . . . . . . . . . . . . 7
2.7 ADVANCED: Database Handling using Trees . . . . . . . . . . . . . . . . . 8
3 Concurrent Programming 10
3.1 An Echo Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 The Process Ring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3 The Process Crossring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
7 Advanced Topics 27
7.1 ADVANCED: List Comprehensions . . . . . . . . . . . . . . . . . . . . . . . 27
7.2 Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.3 Distributed, Replicated Associative Store . . . . . . . . . . . . . . . . . . . 29
2 Sequential Programming
2.1 Evaluating Expressions
sums.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: sums.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(sums).
8 -export([sum/1, sum_interval/2]).
9
10 %% @doc Adds the integers between 1 and N.
11 -spec sum(non_neg_integer()) -> integer().
12 sum(0) ->
13 0;
14 sum(N) ->
15 N + sum(N-1).
16
17 %% @doc Adds the integers between N and M
18 -spec sum_interval(integer(), integer()) -> integer().
19 sum_interval(Max, Max) ->
20 Max;
21 sum_interval(Min, Max) when Min =< Max ->
22 Min + sum_interval(Min + 1, Max).
28 [H|concatenate1(T, Lists)];
29 concatenate1([], Lists) ->
30 concatenate(Lists).
31
32 %% @doc Will reverse the list order.
33 -spec reverse(list()) -> list().
34 reverse(List) ->
35 reverse(List, []).
36 reverse([], Buffer) ->
37 Buffer;
38 reverse([H|T], Buffer) ->
39 reverse(T, [H|Buffer]).
40
41 %% @doc Takes a list and recursively flattens it.
42 -spec flatten(list()) -> list().
43 flatten([H|T]) when is_list(H) ->
44 concatenate([flatten(H), flatten(T)]);
45 flatten([H|T]) ->
46 [H|flatten(T)];
47 flatten([]) ->
48 [].
36
37 %% Splits a list at the point N and returns both parts
38 split(N, List) -> split(N, List, []).
39 split(0, List, Acc) -> {Acc, List};
40 split(N, [H|T], Acc) -> split(N-1, T, [H|Acc]).
41
42 %% Merges two sorted lists in a single one, still sorted.
43 merge([], Right) -> Right;
44 merge(Left, []) -> Left;
45 merge(Left = [L|Ls], Right = [R|Rs]) ->
46 if L =< R -> [L | merge(Ls, Right)];
47 L > R -> [R | merge(Left, Rs)]
48 end.
3 Concurrent Programming
3.1 An Echo Server
echo.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : echo.erl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(echo).
8 -export([start/0, stop/0, listen/0, print/1]).
9
10 -spec start() -> ok.
11 start()->
12 register(echo, spawn(echo, listen, [])),
13 ok.
14
15 %% Prints a term passed as an argument.
16 -spec print(term()) -> ok.
17 print(Message)->
18 echo ! {print, Message},
19 ok.
20
21 %% Stops the echo server.
22 -spec stop() -> ok.
23 stop()->
24 echo ! stop,
25 ok.
26
27 %% The echo server loop
28 -spec listen() -> true.
29 listen()->
30 receive
31 {print, Message} ->
32 io:format("~p~n",[Message]),
33 listen();
34 stop ->
35 true
36 end.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(crossring).
8
9 %% Client Functions
10 -export([start/3]).
11 %% Internal Exports
12 -export([master/3, loop/2]).
13
14 %% @doc Starts the master process which in turn spawns off the
15 %% individual processes which will receive a message.
16 -spec start(pos_integer(), non_neg_integer(), term()) -> pid().
17 start(ProcNum, MsgNum, Message)->
18 spawn(crossring, master, [ProcNum, MsgNum, Message]).
19
20 %% @private This function starts the slave pids and then gets into
21 %% the loop which will send the Message MsgNum times to
22 %% the slaves.
23 -spec master(pos_integer(), non_neg_integer(), term()) -> stop | no_return().
24 master(ProcNum, MsgNum, Message)->
25 ProcLim = round(ProcNum / 2),
26 {MidPid, FirstPid} = start_slaves(ProcNum, ProcLim, self()),
27 master_loop(MsgNum, {first_half, Message}, FirstPid, MidPid).
28
29
30 %% Will start ProcNum slave processes
31 -spec start_slaves(pos_integer(), non_neg_integer(), pid()) -> pid() | {pid(),
pid()}.
32 start_slaves(1, _, Pid)->
33 Pid;
34 %% We cross when we’re on the midpoint process + 1.
35 start_slaves(ProcNum, ProcLim, Pid) when ProcNum =:= ProcLim + 1->
36 %% We spawn the process the first one will send messages to
37 MidPid = spawn(crossring, loop, [ProcNum, Pid]),
38 %% We return it in a tuple, and keep starting the other processes
39 %% after the first (middle) one. The Last spawned Pid (or the second
40 %% element of the crossring) is returned as the second tuple element
41 {MidPid, start_slaves(ProcNum - 1, ProcLim, self())};
42 start_slaves(ProcNum, ProcLim, Pid)->
43 NewPid = spawn(crossring, loop, [ProcNum, Pid]),
44 start_slaves(ProcNum - 1, ProcLim, NewPid).
45
46 %% The master loop will loop MsgNum times sending a message to
47 %% Pid. It will iterate every time it receives the Message it is
48 %% sent to the next process in the ring.
49 -spec master_loop(non_neg_integer(), term(), pid(), pid()) -> stop | no_return
().
50 master_loop(0, _Message, FirstPid, MidPid)->
51 io:format("Process: 1 terminating~n"),
52 MidPid ! FirstPid ! stop;
53 %% Handling the messages on the first half of the crossring
54 master_loop(MsgNum, {first_half, Message}, FirstPid, MidPid) ->
55 FirstPid ! {first_half, Message},
56 receive
57 {first_half, Message} ->
58 io:format("Process: 1 received: ~p halfway through~n",[Message]),
59 master_loop(MsgNum, {second_half, Message}, FirstPid, MidPid)
60 end;
61 %% Handling the messages on the second half of the crossring
62 master_loop(MsgNum, {second_half, Message}, FirstPid, MidPid) ->
63 MidPid ! {second_half, Message},
64 receive
65 {second_half, Message} ->
66 io:format("Process: 1 received: ~p~n",[Message]),
67 master_loop(MsgNum - 1, {first_half, Message}, FirstPid, MidPid)
68 end.
69
70 %% @private This is the slave loop, where upon receiving a message, the
71 %% process forwards it to the next process in the ring. Upon
72 %% receiving stop, it sends the stop message on and terminates.
73 -spec loop(pos_integer(), pid()) -> stop | no_return().
74 loop(ProcNum, Pid)->
75 receive
76 stop ->
77 io:format("Process: ~p terminating~n",[ProcNum]),
78 Pid ! stop;
79 {Part, Message} ->
80 io:format("Process: ~p received: ~p~n", [ProcNum, Message]),
81 Pid ! {Part, Message},
82 loop(ProcNum, Pid)
83 end.
56 %% reply received.
57 -spec sync_call(term()) -> term().
58 sync_call(Msg) ->
59 db ! {request, self(), Msg},
60 receive
61 {reply, Reply} -> Reply
62 end.
63
64 %% Will send an asynchronous reques to the server.
65 -spec async_call(term()) -> ok.
66 async_call(Msg) ->
67 db ! {request, Msg},
68 ok.
69
70 %% Sends a reply back to the client’s synchronous call.
71 -spec reply(pid(), term()) -> {reply, term()}.
72 reply(Pid, Reply) ->
73 Pid ! {reply, Reply}.
74
75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 %% Database Server Loop
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78
79 %% @private Initialises the database and enters the server loop
80 -spec init() -> no_return().
81 init() ->
82 loop(db:new()).
83
84 %% loop(list())
85 %% The database server loop which will iterate and handle
86 %% all requests.
87 -spec loop(db:db()) -> no_return().
88 loop(Db) ->
89 receive
90 {request, {write, Key, Element}} ->
91 NewDb = db:write(Key, Element, Db),
92 loop(NewDb);
93 {request, {delete, Key}} ->
94 NewDb = db:delete(Key, Db),
95 loop(NewDb);
96 {request, Pid, {read, Key}} ->
97 reply(Pid, db:read(Key, Db)),
98 loop(Db);
99 {request, Pid, {match, Element}} ->
100 reply(Pid, db:match(Element, Db)),
101 loop(Db);
102 {request, stop} ->
103 db:destroy(Db)
104 end.
8 %% Client Exports
9 -export([start/0, signal/0, wait/0]).
10
11 %% Internal Exports
12 -export([free/0]).
13
14 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15 %% Client Functions
16 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17
18 %% @doc Will start the mutex semaphore
19 -spec start() -> true.
20 start() ->
21 register(mutex, spawn(mutex, free, [])).
22
23 %% @doc Will free the semaphore currently held by the process
24 -spec signal() -> ok.
25 signal() ->
26 mutex ! {signal, self()},
27 ok.
28
29 %% @doc Will keep the process busy until the semaphore is available.
30 -spec wait() -> ok.
31 wait() ->
32 mutex ! {wait, self()},
33 receive
34 ok -> ok
35 end.
36
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 %% Finite State Machine
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40
41 %% @doc The state where the semaphore is available
42 -spec free() -> no_return().
43 free() ->
44 receive
45 {wait, Pid} ->
46 Pid ! ok,
47 busy(Pid)
48 end.
49
50 %% @doc The semaphore is taken by Pid. Pid is the only process which
51 %% may release it.
52 -spec busy(pid()) -> no_return().
53 busy(Pid) ->
54 receive
55 {signal, Pid} -> free()
56 end.
8
9 %% Internal Exports
10 -export([init/0]).
11
12 %%External Exports
13 -export([start/0, stop/0]).
14 -export([write/2, delete/1, read/1, match/1, lock/0, unlock/0]).
15
16 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17 %% Client Functions
18 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
19
20 %% @doc Starts the database server
21 -spec start() -> ok.
22 start() ->
23 register(db, spawn(my_db_trans, init, [])),
24 ok.
25
26 %% @doc Stops the database server
27 -spec stop() -> ok.
28 stop() ->
29 sync_call(stop).
30
31 %% @doc Inserts an element in the database server
32 -spec write(Key::term(), Val::term()) -> ok.
33 write(Key, Element) ->
34 sync_call({write, Key, Element}).
35
36 %% @doc Removes an element from the database. Will succeed even
37 %% If the element does not exist.
38 -spec delete(Key::term()) -> ok.
39 delete(Key) ->
40 sync_call({delete, Key}).
41
42 %% @doc Will retrieve an element from the database.
43 -spec read(Key::term()) -> {ok, term()} | {error, instance}.
44 read(Key) ->
45 sync_call({read, Key}).
46
47 %% @doc Will return a list of keys which match to the element.
48 -spec match(Val::term()) -> [Key::term()].
49 match(Element) ->
50 sync_call({match, Element}).
51
52 %% @doc Will lock the database for the current caller.
53 -spec lock() -> ok.
54 lock() ->
55 sync_call(lock).
56
57 %% @doc Will unlock the database.
58 -spec unlock() -> ok.
59 unlock() ->
60 sync_call(unlock).
61
62 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63 %% Communication Help Functions
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65
66 %% Will make a synchronous call to the server and return the
67 %% reply received.
68 -spec sync_call(term()) -> term().
69 sync_call(Msg) ->
70 db ! {request, self(), Msg},
71 receive
72 {reply, Reply} -> Reply
73 end.
74
75 %% Sends a reply back to the client’s synchronous call.
76 -spec reply(pid(), term()) -> {reply, term()}.
77 reply(Pid, Reply) ->
78 Pid ! {reply, Reply}.
79
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 %% Database Server Loop
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83
84 %% @private Initialises the database and enters the server loop
85 -spec init() -> no_return().
86 init() ->
87 loop(db:new()).
88
89 %% loop(list())
90 %% The database server loop which will iterate and handle
91 %% all requests.
92 -spec loop(db:db()) -> no_return().
93 loop(Db) ->
94 receive
95 {request, Pid, {write, Key, Element}} ->
96 NewDb = db:write(Key, Element, Db),
97 reply(Pid, ok),
98 loop(NewDb);
99 {request, Pid, {delete, Key}} ->
100 NewDb = db:delete(Key, Db),
101 reply(Pid, ok),
102 loop(NewDb);
103 {request, Pid, {read, Key}} ->
104 reply(Pid, db:read(Key, Db)),
105 loop(Db);
106 {request, Pid, {match, Element}} ->
107 reply(Pid, db:match(Element, Db)),
108 loop(Db);
109 {request, Pid, stop} ->
110 reply(Pid, ok),
111 db:destroy(Db);
112 {request, Pid, lock} ->
113 reply(Pid, ok),
114 locked_loop(Db, Pid)
115 end.
116
117 %% locked_loop(list(), pid())
118 %% The database server loop which will iterate and handle
119 %% all requests from the locking client.
120 -spec locked_loop(db:db(), pid()) -> no_return().
121 locked_loop(Db, Locker) ->
122 receive
123 {request, Locker, {write, Key, Element}} ->
124 NewDb = db:write(Key, Element, Db),
125 reply(Locker, ok),
126 locked_loop(NewDb, Locker);
127 {request, Locker, {delete, Key}} ->
128 NewDb = db:delete(Key, Db),
129 reply(Locker, ok),
56 loop_b()
57 after
58 15000 ->
59 io:format("Pong got bored, exiting.~n"),
60 exit(timeout)
61 end.
7
8 -module(fussball).
9
10 %% Interface
11 -export([start/2, init/2, stop/1, kickoff/1]).
12
13 start(MyCountry, OtherCountry) ->
14 spawn(?MODULE, init, [MyCountry, OtherCountry]),
15 ok.
16
17 stop(Country) ->
18 Country ! stop.
19
20 kickoff(Country) ->
21 Country ! kick,
22 ok.
23
24 init(MyCountry, OtherCountry) ->
25 process_flag(trap_exit, true),
26 register(MyCountry, self()),
27 catch link(whereis(OtherCountry)),
28 loop(MyCountry, OtherCountry).
29
30 loop(MyCountry, OtherCountry) ->
31 receive
32 {’EXIT’, _Pid, Reason} ->
33 io:format("Got exit signal ~p~n", [Reason]),
34 ok;
35 stop ->
36 ok;
37 save ->
38 io:format("~p just saved...~n", [OtherCountry]),
39 loop(MyCountry, OtherCountry);
40 score ->
41 io:format("Oh no! ~p just scored!!~n", [OtherCountry]),
42 loop(MyCountry, OtherCountry);
43 kick ->
44 timer:sleep(500),
45 case rand:uniform(1000) of
46 N when N > 950 ->
47 io:format("~p SAVES! And what a save!!~n", [MyCountry]),
48 OtherCountry ! save,
49 OtherCountry ! kick;
50 N when N > 800 ->
51 io:format("~p SCORES!!~n", [MyCountry]),
52 OtherCountry ! score;
53 _ ->
54 io:format("~p kicks the ball...~n", [MyCountry]),
55 OtherCountry ! kick
56 end,
57 loop(MyCountry, OtherCountry)
58 end.
47 free() ->
48 receive
49 {’EXIT’, Pid, _Reason} ->
50 free();
51 {wait, Pid} ->
52 link(Pid),
53 Pid ! ok,
54 busy(Pid)
55 end.
56
57 %% @doc The semaphore is taken by Pid. Pid is the only process which
58 %% may release it.
59 -spec busy(pid()) -> no_return().
60 busy(Pid) ->
61 receive
62 {’EXIT’, Pid, _Reason} ->
63 free();
64 {signal, Pid} ->
65 unlink(Pid),
66 free()
67 end.
36 end.
37
38 %%% @doc Initialises the supervisor state
39 -spec init() -> ok.
40 init() ->
41 process_flag(trap_exit, true),
42 loop([]).
43
44 %%% loop([child()]) -> ok.
45 %%% child() = {pid(), restar_count(), mod(), func(), [args()]}.
46 %%% restart_count() = integer(). number of times the child has restarted
47 %%% mod() = atom(). the module where the spawned function is located
48 %%% func() = atom(). the function spawned
49 %%% args() = term(). the arguments passed to the function
50 %%% The supervisor loop which handles the incoming client requests
51 %%% and EXIT signals from supervised children.
52 -type child() :: {pid(), non_neg_integer(), atom(), atom(), [term()]}.
53 -spec loop([child()]) -> ok.
54 loop(Children) ->
55 receive
56 {start_child, ClientPid, Mod, Func, Args} ->
57 Pid = spawn_link(Mod, Func, Args),
58 ClientPid ! {ok, Pid},
59 loop([{Pid, 1, Mod, Func, Args}|Children]);
60 {’EXIT’, Pid, normal} ->
61 NewChildren = lists:keydelete(Pid, 1, Children),
62 loop(NewChildren);
63 {’EXIT’, Pid, Reason} ->
64 NewChildren = lists:keydelete(Pid, 1, Children),
65 {value, Child} = lists:keysearch(Pid, 1, Children),
66 {Pid, Count, Mod, Func, Args} = Child,
67 error_message(Pid, Count, Reason, Mod, Func, Args),
68 NewPid = spawn_link(Mod, Func, Args),
69 loop([{NewPid, Count + 1, Mod, Func, Args}|NewChildren]);
70 stop ->
71 kill_children(Children)
72 end.
73
74 %%% Kills all the children in the supervision tree.
75 -spec kill_children([child()]) -> ok.
76 kill_children([{Pid, _Count, _Mod, _Func, _Args}|Children]) ->
77 exit(Pid, kill),
78 kill_children(Children);
79 kill_children([]) ->
80 ok.
81
82 %%% Prints an error message for the child which died.
83 -spec error_message(pid(), non_neg_integer(), term(), atom(), atom(), [term()])
84 -> ok.
85 error_message(Pid, Count, Reason, Mod, Func, Args) ->
86 io:format("~50c~n",[$-]),
87 io:format("Error: Process ~p Terminated ~p time(s)~n",[Pid, Count]),
88 io:format(" Reason for termination:~p~n",[Reason]),
89 io:format(" Restarting with ~p:~p/~p~n",[Mod,Func,length(Args)]),
90 io:format("~50c~n",[$-]).
db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: db.erl
3 %%% @author [email protected]
4 %%% @copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(db).
8 -export([new/0, write/3, delete/2, read/2, match/2, destroy/1]).
9 -include("db.hrl").
10
11 -export_type([db/0]).
12 -type db() :: list().
13
14 %% @doc Create a new database
15 -spec new() -> db().
16 new() -> [].
17
18 %% @doc Insert a new element in the database
19 -spec write(Key::term(), Val::term(), db()) -> db().
20 write(Key, Element, Db) -> [#data{key = Key, value = Element}|Db].
21
22 %% @doc Remove an element from the database
23 -spec delete(Key::term(), db()) -> db().
24 delete(Key, [#data{key = Key}|Db]) ->
25 Db;
26 delete(Key, [Record|Db]) ->
27 [Record|delete(Key, Db)];
28 delete(_Key, []) ->
29 [].
30
31 %% @doc Retrieve the first element in the database with a matching key
32 -spec read(Key::term(), db()) -> {ok, term()} | {error, instance}.
33 read(Key, [#data{key = Key, value = Element}|_Db]) ->
34 {ok, Element};
35 read(Key, [_Record|Db]) ->
36 read(Key, Db);
37 read(_Key, []) ->
38 {error, instance}.
39
40 %% @doc Return all the keys whose values match the given element.
41 -spec match(Val::term(), db()) -> [term()].
42 match(Element, [#data{key = Key, value = Element}|Db]) ->
43 [Key|match(Element, Db)];
44 match(Element, [_Record|Db]) ->
45 match(Element, Db);
7 Advanced Topics
7.1 ADVANCED: List Comprehensions
lc.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : lc.erl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(lc).
8
9 -export([three/0, filtersquare/1, intersection/2, disjunction/2]).
10
11 three() ->
12 [X || X <- lists:seq(1,10), X rem 3 == 0].
13
14 filtersquare(List) ->
15 [X*X || X <- List, is_integer(X)].
16
17 intersection(List1, List2) ->
18 [H || H <- List1, lists:member(H, List2)].
19
20 disjunction(List1, List2) ->
21 Intersection = intersection(List1, List2),
22 [H || H <- List1 ++ List2, not lists:member(H, Intersection)].
7.2 Distribution
db.hrl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : db.hrl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -record(data, {key, value}).
db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: db.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 %%% NOTE: TO USE, CHANGE FILE NAME FROM db.erl.dist TO db.erl AND
8 %%% COMPILE
9
10 -module(db).
11 -define(NODE, ’a@localhost’).
12 -define(NAME, ?MODULE).
13 -define(SERVER, {?NAME, ?NODE}).
14
15 %% Client Function Exports
16 -export([new/0, write/3, delete/2, read/2, match/2, destroy/1]).
17 -include("db.hrl").
18 -export_type([db/0]).
19 -type db() :: ?SERVER.
20
21 %% @doc Create a new database.
22 -spec new() -> db().
23 new() ->
24 Pid = spawn(?NODE, fun() -> init() end),
25 register(?NAME, Pid),
26 ?SERVER.
27
28 %% @doc Insert a new element in the database
29 -spec write(Key::term(), Val::term(), db()) -> db().
30 write(Key, Element, Db) ->
31 send(Db, {write, Key, Element}).
32
33 %% @doc Remove an element from the database
34 -spec delete(Key::term(), db()) -> db().
35 delete(Key, Db) ->
36 send(Db, {delete, Key}).
37
38 %% @doc Retrieve the first element in the database with a matching key
39 -spec read(Key::term(), db()) -> {ok, term()} | {error, instance}.
40 read(Key, Db) ->
41 send(Db, {read, Key}).
42
43 %% @doc Return all the keys whose values match the given element.
44 -spec match(Val::term(), db()) -> [term()].
45 match(Element, Db) ->
46 send(Db, {match, Element}).
47
48 %% @doc Deletes the database.
49 -spec destroy(db()) -> ok.
50 destroy(Db) ->
51 send(Db, destroy).
52
53 %% SERVER
54 send(To, Msg) ->
55 To ! {self(), Msg},
56 receive
57 done -> % Dropping the message to maintain the API
58 To;
59 Reply ->
60 Reply
61 after 5000 -> erlang:error(timeout)
62 end.
63
64 reply(To, Msg) ->
65 To ! Msg.
66
67 init() ->
68 loop(ets:new(db, [{keypos, #data.key}])).
69
70 loop(Db) ->
71 receive
72 {From, {write, Key, Element}} ->
73 ets:insert(Db, #data{key = Key, value = Element}),
74 reply(From, done),
75 loop(Db);
76 {From, {delete, Key}} ->
77 ets:delete(Db, Key),
78 reply(From, done),
79 loop(Db);
80 {From, {read, Key}} ->