1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
-module(d).
-export([start/0, stop/0]).
-export([store/2, store/3, move/2,
location/1, who_are_at/1, who_are_older/1,
size/0]).
-export([init/0]). % spawn
-record(person, {name, age, location, moved=false}).
%%%----------------------------------------------------------------------
%%% User interface functions
%%%----------------------------------------------------------------------
%%% start() -> pid()
start() ->
spawn(?MODULE, init, []).
%%% stop()
stop() ->
arne ! stop.
%%% store(Name, Location) ->
%%% store(Name, Age, Location) -> ok | {error,Reason}
%%% Name = Location = atom()
%%% Age = integer()
%%% Reason = not_started | no_response | {internal_error,term()}
store(Name, Location) ->
store(Name, ?AGE, Location).
store(Name, Age, Location) when atom(Name), integer(Age), atom(Location) ->
send({store, Name, Age, Location}).
%%% move(OldLocation, NewLocation) -> Names | {error,Reason}
%%% OldLocation = NewLocation = atom()
%%% Names = [Name]
%%% Name = atom()
%%% Reason = not_started | no_response | {internal_error,term()}
move(OldLocation, NewLocation) ->
send({move, OldLocation, NewLocation}).
%%% location(Name) -> Location | no_such_person | {error,Reason}
%%% Name = atom()
%%% Reason = not_started | no_response | {internal_error,term()}
location(Name) when atom(Name) ->
send({location, Name}).
%%% who_are_at(Location) -> Names | {error,Reason}
%%% Location = atom()
%%% Names = [Name]
%%% Name = atom()
%%% Reason = not_started | no_response | {internal_error,term()}
who_are_at(Location) when atom(Location) ->
send({who_are_at, Location}).
%%% who_are_older(Age) -> Names | {error,Reason}
%%% Age = integer()
%%% Names = [Name]
%%% Name = atom()
%%% Reason = not_started | no_response | {internal_error,term()}
who_are_older(Age) when integer(Age) ->
send({who_are_older, Age}).
%%% size() -> N | {error,Reason}
%%% N = integer()
%%% Reason = not_started | no_response | {internal_error,term()}
size() ->
send(size).
%%%----------------------------------------------------------------------
%%% Main loop
%%%----------------------------------------------------------------------
send(Request) ->
Pid = whereis(arne),
if
Pid==undefined ->
{error, not_started};
true ->
send(Pid, Request)
end.
send(Pid, Request) ->
Pid ! {request, self(), Request},
receive
{reply, Reply} ->
Reply
after
1000 ->
{error, no_response}
end.
init() ->
register(arne, self()),
loop([]).
loop(Db) ->
receive
stop ->
true;
{request, From, Request} ->
case catch handle(Request, Db) of
{reply, Reply, NewDb} ->
From ! {reply, Reply},
loop(NewDb);
{'EXIT', Reason} ->
From ! {reply, {error, {internal_error, Reason}}},
loop(Db)
end
end.
%%%----------------------------------------------------------------------
%%% DB functionality
%%%----------------------------------------------------------------------
handle({store, Name, Age, Location}, Db) ->
{reply, ok, [#person{name=Name, age=Age, location=Location} | Db]};
handle({move, OldLocation, NewLocation}, Db) ->
{Names, NewDb} = move(OldLocation, NewLocation, Db, [], []),
{reply, Names, NewDb};
handle({location, Name}, Db) ->
case lists:keysearch(Name, #person.name, Db) of
{value, #person{location=Location}} when atom(Location) ->
{reply, Location, Db};
false ->
{reply, no_such_name, Db}
end;
handle({who_are_at, Location}, Db) ->
Result = lists:foldl(fun(Person, Names) ->
case Person#person.location of
Location ->
[Person#person.name | Names];
_OtherLocation ->
Names
end
end,
[],
Db),
{reply, Result, Db};
handle({who_are_older, Old}, Db) ->
Result = [Name || {person,Name,Age,Location} <- Db,
Age>Old],
{reply, Result, Db};
handle(size, Db) ->
Result = count(Db, 0), {reply, Result, Db}.
count([H|T], N) ->
count(T, N+1);
count([], N) ->
N.
move(OldLoc, NewLoc, [#person{location=OldLoc} = Person|T], Db, Names) ->
NewPerson = Person#person{location=NewLoc,
moved=true},
NewNames = [Person#person.name|Names],
move(OldLoc, NewLoc, T, [NewPerson|Db], NewNames);
move(OldLoc, NewLoc, [Person|T], Db, Names) ->
move(OldLoc, NewLoc, T, [Person|Db], Names);
move(OldLoc, NewLoc, [], Db, Names) ->
{lists:reverse(Names), lists:reverse(Db)}.
|