我可以修改由带有外部函数的 gen_server 管理的地图吗?
Can I modify a map that is managed with a gen_server with external functions?
我有一个由具有 gen_server 行为的模块管理的地图,我可以在其中添加、删除和更新键->值。
我还有一个包含一些例程和子例程的主模块,我在其中根据映射中的键-> 值进行操作。我的问题是我试图在我的模块执行期间修改地图,但我没有得到任何答案。
这是我的主要模块的结构示例:
-export([
go/0,
add_belief/1
]).
go()->
bs:start_link(),
collect_bottles(0).
collect_bottles(Total) ->
case {bs:is_belief(holding), bs:is_belief(over_drop)} of
{true, true} -> drop_and_leave();
{true,false} -> get_to_drop();
{false, _} -> get_bottle()
end.
get_bottle()->
io:format("Getting bottle.~n"),
case {bs:get_belief(see)} of
{true} -> collect_bottles(bs:get_belief(collected));
{false} ->move(),
get_bottle()
end.
move(Dist)->
io:format("Start moving...~n"),
timer:sleep(5000).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%% GOD FUNCTIONS %%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
add_belief(Belief)->
bs:add_belief(Belief).
bs:add_belief(Belief)
的代码是:
add_belief(Belief)->
gen_server:cast(?MODULE,{add,Belief}).
并且在 gen_server
函数中:
handle_cast({add,{Key,Value}},State)->
io:format("Belief added: ~p.~n",[{Key,Value}]),
{noreply, maps:put(Key,Value,State)};
当我 运行 我的脚本时,我得到:
tr:go().
Getting bottle.
Start moving...
Getting bottle.
Start moving...
而且我不能不使用另一个函数(我想使用 add_belief({see,bottle})
来跳出循环。
Can I modify a map that is managed with a gen_server with external
functions?
是的,这是证明:
$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> c(tr).
{ok,tr}
2> c(bs).
{ok,bs}
3> c(env).
{ok,env}
4> tr:test().
tr:get_bottle(): getting bottle
<0.74.0>
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
5> env:get_state().
env: get_state(): 10
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
6> bs:get_state().
bs:get_state(): #{}
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
7> bs:add_belief({holding, []}).
Adding belief: {holding,[]}
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
8> bs:get_state().
bs:get_state(): #{holding=>[]}
ok
tr:get_bottle(): getting bottle
9>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
$
如果我改为更改我的 bs:erl
文件以调用 gen_server:start_link()
,如下所示:
start_link() ->
gen_server:start_link(
%%{local, ?MODULE},
?MODULE, [], []
).
然后是这样:
1> c(tr).
{ok,tr}
2> c(bs).
{ok,bs}
3> c(env).
{ok,env}
4> tr:test().
tr:get_bottle(): getting bottle
<0.74.0>
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
5> bs:add_belief({holding, []}).
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
6> bs:get_state().
** exception exit: {noproc,{gen_server,call,[bs,get_state]}}
in function gen_server:call/2 (gen_server.erl, line 204)
in call from bs:get_state/0 (bs.erl, line 40)
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
来自gen_server docs 关于gen_server:start_link()
:
The first argument, {local, ch3}, specifies the name. The gen_server
is then locally registered as ch3.
If the name is omitted, the gen_server is not registered. Instead its
pid must be used. The name can also be given as {global, Name}, in
which case the gen_server is registered using global:register_name/2.
如果您没有指定 {local, ?MODULE}
作为 gen_server:start_link()
的参数,那么您必须像这样调用 gen_server:call()
:
gen_server:call(ServerPid, Request)
要获取 ServerPid,您需要执行以下操作:
start_link() ->
{ok, ServerPid} = gen_server:start_link(
%%{local, ?MODULE},
?MODULE, [], []
),
ServerPid.
从上面的最后一个 shell 会话来看,如果您不指定 {local, ServerName}
——因此您不会注册服务器名称——并且您调用 gen_server:cast(?MODULE, ...)
它不会导致错误,但是如果你调用 gen_server:call(?MODULE...)
你会得到一个错误。对我来说,如果在服务器未注册的情况下在这两种情况下都出现错误,这似乎很方便——演员表的 ok
的 return 值非常具有误导性。
tr.erl
:
-module(tr).
%%-compile(export_all).
-export([go/0, test/0]).
go() ->
bs:start_link(),
env:start_link(),
collect_bottles(0).
collect_bottles(_Total) ->
get_bottle().
get_bottle() ->
io:format("tr:get_bottle(): getting bottle~n"),
timer:sleep(3000),
get_bottle().
test() ->
spawn(tr, go, []).
bs.erl
:
-module(bs).
%%-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2]).
-export([handle_info/2, terminate/2, code_change/3]).
-export([start_link/0, add_belief/1, get_state/0, stop/0]).
%%Internal server functions:
init([]) ->
{ok, #{}}. %%<******** INITIALIZE STATE WITH AN EMPTY MAP
handle_cast({add, {Key, Val}=Belief}, State) ->
io:format("Adding belief: ~w~n", [Belief]),
{ noreply, maps:put(Key, Val, State) }.
handle_call(get_state, _From, State) ->
{reply, State, State}.
% -----
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%External interface:
start_link() ->
gen_server:start_link(
{local, ?MODULE},
?MODULE, [], []
).
add_belief(Belief) -> %%<******* EXTERNAL FUNCTION THAT MODIFIES A MAP
gen_server:cast(?MODULE, {add, Belief}).
get_state() ->
State = gen_server:call(?MODULE, get_state),
io:format("bs:get_state(): ~w~n", [State]).
stop() ->
gen_server:stop(?MODULE).
env.erl
:
-module(env).
%%-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2]).
-export([handle_info/2, terminate/2, code_change/3]).
-export([start_link/0, get_state/0, stop/0]).
%%Internal server functions:
init([]) ->
{ok, 10}. %%<***** INITIALIZE STATE WITH 10
handle_call(get_state, _From, State) ->
{reply, State, State}.
%% ------
handle_cast(_Request, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%External interface:
start_link() ->
gen_server:start_link(
{local, ?MODULE},
?MODULE, [], []
).
get_state() ->
State = gen_server:call(?MODULE, get_state),
io:format("env: get_state(): ~w~n", [State]).
stop() ->
gen_server:stop(?MODULE).
我有一个由具有 gen_server 行为的模块管理的地图,我可以在其中添加、删除和更新键->值。
我还有一个包含一些例程和子例程的主模块,我在其中根据映射中的键-> 值进行操作。我的问题是我试图在我的模块执行期间修改地图,但我没有得到任何答案。
这是我的主要模块的结构示例:
-export([
go/0,
add_belief/1
]).
go()->
bs:start_link(),
collect_bottles(0).
collect_bottles(Total) ->
case {bs:is_belief(holding), bs:is_belief(over_drop)} of
{true, true} -> drop_and_leave();
{true,false} -> get_to_drop();
{false, _} -> get_bottle()
end.
get_bottle()->
io:format("Getting bottle.~n"),
case {bs:get_belief(see)} of
{true} -> collect_bottles(bs:get_belief(collected));
{false} ->move(),
get_bottle()
end.
move(Dist)->
io:format("Start moving...~n"),
timer:sleep(5000).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%% GOD FUNCTIONS %%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
add_belief(Belief)->
bs:add_belief(Belief).
bs:add_belief(Belief)
的代码是:
add_belief(Belief)->
gen_server:cast(?MODULE,{add,Belief}).
并且在 gen_server
函数中:
handle_cast({add,{Key,Value}},State)->
io:format("Belief added: ~p.~n",[{Key,Value}]),
{noreply, maps:put(Key,Value,State)};
当我 运行 我的脚本时,我得到:
tr:go().
Getting bottle.
Start moving...
Getting bottle.
Start moving...
而且我不能不使用另一个函数(我想使用 add_belief({see,bottle})
来跳出循环。
Can I modify a map that is managed with a gen_server with external functions?
是的,这是证明:
$ erl
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2 (abort with ^G)
1> c(tr).
{ok,tr}
2> c(bs).
{ok,bs}
3> c(env).
{ok,env}
4> tr:test().
tr:get_bottle(): getting bottle
<0.74.0>
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
5> env:get_state().
env: get_state(): 10
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
6> bs:get_state().
bs:get_state(): #{}
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
7> bs:add_belief({holding, []}).
Adding belief: {holding,[]}
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
8> bs:get_state().
bs:get_state(): #{holding=>[]}
ok
tr:get_bottle(): getting bottle
9>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
$
如果我改为更改我的 bs:erl
文件以调用 gen_server:start_link()
,如下所示:
start_link() ->
gen_server:start_link(
%%{local, ?MODULE},
?MODULE, [], []
).
然后是这样:
1> c(tr).
{ok,tr}
2> c(bs).
{ok,bs}
3> c(env).
{ok,env}
4> tr:test().
tr:get_bottle(): getting bottle
<0.74.0>
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
5> bs:add_belief({holding, []}).
ok
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
6> bs:get_state().
** exception exit: {noproc,{gen_server,call,[bs,get_state]}}
in function gen_server:call/2 (gen_server.erl, line 204)
in call from bs:get_state/0 (bs.erl, line 40)
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
tr:get_bottle(): getting bottle
来自gen_server docs 关于gen_server:start_link()
:
The first argument, {local, ch3}, specifies the name. The gen_server is then locally registered as ch3.
If the name is omitted, the gen_server is not registered. Instead its pid must be used. The name can also be given as {global, Name}, in which case the gen_server is registered using global:register_name/2.
如果您没有指定 {local, ?MODULE}
作为 gen_server:start_link()
的参数,那么您必须像这样调用 gen_server:call()
:
gen_server:call(ServerPid, Request)
要获取 ServerPid,您需要执行以下操作:
start_link() ->
{ok, ServerPid} = gen_server:start_link(
%%{local, ?MODULE},
?MODULE, [], []
),
ServerPid.
从上面的最后一个 shell 会话来看,如果您不指定 {local, ServerName}
——因此您不会注册服务器名称——并且您调用 gen_server:cast(?MODULE, ...)
它不会导致错误,但是如果你调用 gen_server:call(?MODULE...)
你会得到一个错误。对我来说,如果在服务器未注册的情况下在这两种情况下都出现错误,这似乎很方便——演员表的 ok
的 return 值非常具有误导性。
tr.erl
:
-module(tr).
%%-compile(export_all).
-export([go/0, test/0]).
go() ->
bs:start_link(),
env:start_link(),
collect_bottles(0).
collect_bottles(_Total) ->
get_bottle().
get_bottle() ->
io:format("tr:get_bottle(): getting bottle~n"),
timer:sleep(3000),
get_bottle().
test() ->
spawn(tr, go, []).
bs.erl
:
-module(bs).
%%-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2]).
-export([handle_info/2, terminate/2, code_change/3]).
-export([start_link/0, add_belief/1, get_state/0, stop/0]).
%%Internal server functions:
init([]) ->
{ok, #{}}. %%<******** INITIALIZE STATE WITH AN EMPTY MAP
handle_cast({add, {Key, Val}=Belief}, State) ->
io:format("Adding belief: ~w~n", [Belief]),
{ noreply, maps:put(Key, Val, State) }.
handle_call(get_state, _From, State) ->
{reply, State, State}.
% -----
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%External interface:
start_link() ->
gen_server:start_link(
{local, ?MODULE},
?MODULE, [], []
).
add_belief(Belief) -> %%<******* EXTERNAL FUNCTION THAT MODIFIES A MAP
gen_server:cast(?MODULE, {add, Belief}).
get_state() ->
State = gen_server:call(?MODULE, get_state),
io:format("bs:get_state(): ~w~n", [State]).
stop() ->
gen_server:stop(?MODULE).
env.erl
:
-module(env).
%%-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2]).
-export([handle_info/2, terminate/2, code_change/3]).
-export([start_link/0, get_state/0, stop/0]).
%%Internal server functions:
init([]) ->
{ok, 10}. %%<***** INITIALIZE STATE WITH 10
handle_call(get_state, _From, State) ->
{reply, State, State}.
%% ------
handle_cast(_Request, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%External interface:
start_link() ->
gen_server:start_link(
{local, ?MODULE},
?MODULE, [], []
).
get_state() ->
State = gen_server:call(?MODULE, get_state),
io:format("env: get_state(): ~w~n", [State]).
stop() ->
gen_server:stop(?MODULE).