如何在 Erlang 中多次从生成的进程中接收值?
How can I receive value from spawned process multiple times in Erlang?
我想通过 'receive' 从我调用的函数接收多个值。一半代码如下:
-module(b).
-export([step13/3,step7/4,run/0]).
step13(P,Ev,Pid) ->
Lst = lists:nth(P,Ev),
receive
{E} ->
List = Lst ++ [E], L = lists:usort(List)
end,
Edgev = lists:sublist(Ev,P-1) ++ [L] ++ lists:nthtail(P,Ev),
Pid ! {Edgev}.
step7(0,_,_,_) ->
io:fwrite( "Step7 done");
step7(V,R,Ev,Parent) ->
case (V == R) of
true -> io:fwrite( "Root vertex, so leaving ~n");
false -> E = {V,V},
io:fwrite( "For the vertex is ~w ~n", [V] ),
io:fwrite( "New edge is ~w ~n", [E] ),
P = lists:nth(V,Parent),
Pid = spawn(b,step13,[P,Ev,self()]),
Pid ! {E}
end,
case (lists:member(V,Parent) == true) of
true ->
receive
{Edgev} ->
io:fwrite( "Ev now is ~w ~n", [Edgev] )
end;
false -> io:fwrite( "" )
end,
step7(V-1,R,Ev,Parent).
run() ->
V = 4,
Ev = [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] ,
R = 1,
Parent = [0,1,4,1],
step7(V,R,Ev,Parent).
(对于这种面向问题的代码而不是通用代码表示歉意,代码的清理弄乱了一个或另一个东西,我在下面解释代码)
解释:
所以,最初, Ev is [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] , R is 1(always fixed), V is 4, Parent is [0,1,4,1]
step7函数将P计算为Parent的第V个元素并调用step13函数,每当V不等于R时,将元组{V,V}发送给函数step13。(需要注意的是,step13函数是通过传递参数调用的Ev 和 P 已经)
step13 函数执行以下操作:它用接收到的元组 {V,V} 替换 Ev 的第 P 个索引。
现在,转折点来了。由于Parent中有重复项,我们可以看到两个1,收到的值只是1中的一个,因为step7循环由于自递归调用step7(V-1,...).
What output I am getting is:
Ev now is [[{1,2},{1,3},{1,4},{2,2}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
Ev now is [[{1,2},{1,3},{1,4},{4,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
But what I want is:
Ev now is [[{1,2},{1,3},{1,4},{4,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
Ev now is [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
Ev now is [[{1,2},{1,3},{1,4},{2,2}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
顺序可能会改变,我不需要顺序。
所以,基本上,我想生成,并从生成的进程中接收原始调用函数中的所有计算值。
我怀疑自己到底讲清楚了多少,欢迎评论问我,我会解惑的。非常感谢,我学习Erlang已经有1个星期了,我还是个新手。
step7递归调用5次,参数V等于4,3,2,1,0。
在每次调用时,您都会生成进程 step13,它将发回一条消息 {Edgev}。每个 step13 进程将不带任何条件地发回一条且仅一条消息。
在step7中,step13进程启动后,你有两个分支:
- 如果 V 在父级中(只有当 V 为 4 或 1 时才会发生),您将进入接收块以等待消息。
- 否则你什么都不做
- 列表项
在第一次调用时,V == 4
,您收到来自 step13 进程的消息,读取它并将其从邮箱中删除
在下次调用时,V == 3
,您收到来自步骤 13 进程的消息,但您没有阅读它,因此它留在邮箱中。
下次与 V == 2
通话时相同
接下来,V == 1
,你收到来自step13进程的消息,读取邮箱,你得到了V等于3时发送的消息。为V发送的消息= 2 和 1 仍在邮箱中。
最后,在下一次调用时,V == 0
程序退出,邮箱仍然是满的。
我在你的代码中发现了以下问题
条件 (lists:member(V,Parent) == true)
仅对 V ==4 和 V ==1 为真。您只能收到 2 条消息,但您希望收到 3 条消息。
当您将接收块置于条件下时,您在邮箱中留下了不需要的消息,这些消息将在以后读取以代替预期的消息(我不明白你想做什么,但这是我的猜测) 您应该先接收消息然后执行测试,或者,添加对调用的引用,将其发送回 return 消息并有条件地接收带有此引用的消息(我认为这是一个糟糕的解决方案,因为它不会清空邮箱)。
Step7 启动一个进程并等待它的响应。除非真正的代码应该处理其间的其他数据,否则它是没有用的。一个简单的调用会更有效率。
[编辑]
根据您的评论和阅读您的代码,我做出了第一个假设
- 你只想在V = 4,3,2时处理函数step13
- 收集所有结果
如果这是正确的,我对您的代码进行了第一次修改,其中 step7 有 3 个不同的步骤
- 生成所有 step13 进程
- 收集所有回复
- return结果
这是逻辑顺序,但代码中的步骤通常以相反的顺序出现:
-module(b).
-export([step13/3,step7/6,run/0]).
step13(P,Ev,Pid) ->
Lst = lists:nth(P,Ev),
receive
{E} ->
List = Lst ++ [E], L = lists:usort(List)
end,
Edgev = lists:sublist(Ev,P-1) ++ [L] ++ lists:nthtail(P,Ev),
Pid ! Edgev.
% step7(CurrentVertex,RootVertex,Edges,Parent,NumberOfResponsesExpected,Response)
% all vertices has been processed, all answers received
step7(0,_,_,_,0,Resp) ->
Resp;
% all vertices has been processed, waiting for answers
step7(0,_,_,_,NbResp,Resp) ->
receive
Edgev -> step7(0,0,0,0,NbResp-1,[Edgev|Resp])
end;
% remaining vertices to proceed
step7(V,R,Ev,Parent,NbResp,Resp) ->
NewNbResp = case (V == R) of
true -> NbResp;
false -> E = {V,V},
P = lists:nth(V,Parent),
Pid = spawn(b,step13,[P,Ev,self()]),
Pid ! {E},
NbResp + 1
end,
step7(V-1,R,Ev,Parent,NewNbResp,Resp).
run() ->
V = 4,
Ev = [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] ,
R = 1,
Parent = [0,1,4,1],
step7(V,R,Ev,Parent,0,[]).
或者我觉得这个版本更干净
-module(b).
-export([run/0]).
step13(P,Ev,Pid,E) ->
% the usage of a spawned process makes sense if the real algorithm may take time
List = lists:usort(lists:nth(P,Ev) ++ [E]),
Pid ! {self(),lists:sublist(Ev,P-1) ++ [List] ++ lists:nthtail(P,Ev)}.
% spawn processes
step7(V,R,Ev,Parent) ->
Me = self(),
F = fun(X) -> spawn(fun() -> step13(lists:nth(X,Parent),Ev,Me,{X,X}) end) end,
Pids = [F(X) || X <- lists:seq(1,V), X =/= R],
% another case were spawning processes may be useful is if you can have significant code here
receiveAnswers(Pids,[]).
% receive answers
receiveAnswers([],R) -> R;
receiveAnswers([H|T],R) ->
receive
% H is one of the Pids of the step13 processes.
% Use it to guarantee the message comes from an expected process
{H,Edgev} -> receiveAnswers(T,[Edgev|R])
end.
run() ->
V = 4,
Ev = [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] ,
R = 1,
Parent = [0,1,4,1],
step7(V,R,Ev,Parent).
我想通过 'receive' 从我调用的函数接收多个值。一半代码如下:
-module(b).
-export([step13/3,step7/4,run/0]).
step13(P,Ev,Pid) ->
Lst = lists:nth(P,Ev),
receive
{E} ->
List = Lst ++ [E], L = lists:usort(List)
end,
Edgev = lists:sublist(Ev,P-1) ++ [L] ++ lists:nthtail(P,Ev),
Pid ! {Edgev}.
step7(0,_,_,_) ->
io:fwrite( "Step7 done");
step7(V,R,Ev,Parent) ->
case (V == R) of
true -> io:fwrite( "Root vertex, so leaving ~n");
false -> E = {V,V},
io:fwrite( "For the vertex is ~w ~n", [V] ),
io:fwrite( "New edge is ~w ~n", [E] ),
P = lists:nth(V,Parent),
Pid = spawn(b,step13,[P,Ev,self()]),
Pid ! {E}
end,
case (lists:member(V,Parent) == true) of
true ->
receive
{Edgev} ->
io:fwrite( "Ev now is ~w ~n", [Edgev] )
end;
false -> io:fwrite( "" )
end,
step7(V-1,R,Ev,Parent).
run() ->
V = 4,
Ev = [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] ,
R = 1,
Parent = [0,1,4,1],
step7(V,R,Ev,Parent).
(对于这种面向问题的代码而不是通用代码表示歉意,代码的清理弄乱了一个或另一个东西,我在下面解释代码)
解释:
所以,最初, Ev is [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] , R is 1(always fixed), V is 4, Parent is [0,1,4,1]
step7函数将P计算为Parent的第V个元素并调用step13函数,每当V不等于R时,将元组{V,V}发送给函数step13。(需要注意的是,step13函数是通过传递参数调用的Ev 和 P 已经)
step13 函数执行以下操作:它用接收到的元组 {V,V} 替换 Ev 的第 P 个索引。
现在,转折点来了。由于Parent中有重复项,我们可以看到两个1,收到的值只是1中的一个,因为step7循环由于自递归调用step7(V-1,...).
What output I am getting is:
Ev now is [[{1,2},{1,3},{1,4},{2,2}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
Ev now is [[{1,2},{1,3},{1,4},{4,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
But what I want is:
Ev now is [[{1,2},{1,3},{1,4},{4,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
Ev now is [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
Ev now is [[{1,2},{1,3},{1,4},{2,2}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]]
顺序可能会改变,我不需要顺序。
所以,基本上,我想生成,并从生成的进程中接收原始调用函数中的所有计算值。
我怀疑自己到底讲清楚了多少,欢迎评论问我,我会解惑的。非常感谢,我学习Erlang已经有1个星期了,我还是个新手。
step7递归调用5次,参数V等于4,3,2,1,0。
在每次调用时,您都会生成进程 step13,它将发回一条消息 {Edgev}。每个 step13 进程将不带任何条件地发回一条且仅一条消息。
在step7中,step13进程启动后,你有两个分支:
- 如果 V 在父级中(只有当 V 为 4 或 1 时才会发生),您将进入接收块以等待消息。
- 否则你什么都不做
- 列表项
在第一次调用时,
V == 4
,您收到来自 step13 进程的消息,读取它并将其从邮箱中删除在下次调用时,
V == 3
,您收到来自步骤 13 进程的消息,但您没有阅读它,因此它留在邮箱中。下次与
通话时相同V == 2
接下来,
V == 1
,你收到来自step13进程的消息,读取邮箱,你得到了V等于3时发送的消息。为V发送的消息= 2 和 1 仍在邮箱中。最后,在下一次调用时,
V == 0
程序退出,邮箱仍然是满的。
我在你的代码中发现了以下问题
条件 (lists:member(V,Parent) == true)
仅对 V ==4 和 V ==1 为真。您只能收到 2 条消息,但您希望收到 3 条消息。
当您将接收块置于条件下时,您在邮箱中留下了不需要的消息,这些消息将在以后读取以代替预期的消息(我不明白你想做什么,但这是我的猜测) 您应该先接收消息然后执行测试,或者,添加对调用的引用,将其发送回 return 消息并有条件地接收带有此引用的消息(我认为这是一个糟糕的解决方案,因为它不会清空邮箱)。
Step7 启动一个进程并等待它的响应。除非真正的代码应该处理其间的其他数据,否则它是没有用的。一个简单的调用会更有效率。
[编辑]
根据您的评论和阅读您的代码,我做出了第一个假设
- 你只想在V = 4,3,2时处理函数step13
- 收集所有结果
如果这是正确的,我对您的代码进行了第一次修改,其中 step7 有 3 个不同的步骤
- 生成所有 step13 进程
- 收集所有回复
- return结果
这是逻辑顺序,但代码中的步骤通常以相反的顺序出现:
-module(b).
-export([step13/3,step7/6,run/0]).
step13(P,Ev,Pid) ->
Lst = lists:nth(P,Ev),
receive
{E} ->
List = Lst ++ [E], L = lists:usort(List)
end,
Edgev = lists:sublist(Ev,P-1) ++ [L] ++ lists:nthtail(P,Ev),
Pid ! Edgev.
% step7(CurrentVertex,RootVertex,Edges,Parent,NumberOfResponsesExpected,Response)
% all vertices has been processed, all answers received
step7(0,_,_,_,0,Resp) ->
Resp;
% all vertices has been processed, waiting for answers
step7(0,_,_,_,NbResp,Resp) ->
receive
Edgev -> step7(0,0,0,0,NbResp-1,[Edgev|Resp])
end;
% remaining vertices to proceed
step7(V,R,Ev,Parent,NbResp,Resp) ->
NewNbResp = case (V == R) of
true -> NbResp;
false -> E = {V,V},
P = lists:nth(V,Parent),
Pid = spawn(b,step13,[P,Ev,self()]),
Pid ! {E},
NbResp + 1
end,
step7(V-1,R,Ev,Parent,NewNbResp,Resp).
run() ->
V = 4,
Ev = [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] ,
R = 1,
Parent = [0,1,4,1],
step7(V,R,Ev,Parent,0,[]).
或者我觉得这个版本更干净
-module(b).
-export([run/0]).
step13(P,Ev,Pid,E) ->
% the usage of a spawned process makes sense if the real algorithm may take time
List = lists:usort(lists:nth(P,Ev) ++ [E]),
Pid ! {self(),lists:sublist(Ev,P-1) ++ [List] ++ lists:nthtail(P,Ev)}.
% spawn processes
step7(V,R,Ev,Parent) ->
Me = self(),
F = fun(X) -> spawn(fun() -> step13(lists:nth(X,Parent),Ev,Me,{X,X}) end) end,
Pids = [F(X) || X <- lists:seq(1,V), X =/= R],
% another case were spawning processes may be useful is if you can have significant code here
receiveAnswers(Pids,[]).
% receive answers
receiveAnswers([],R) -> R;
receiveAnswers([H|T],R) ->
receive
% H is one of the Pids of the step13 processes.
% Use it to guarantee the message comes from an expected process
{H,Edgev} -> receiveAnswers(T,[Edgev|R])
end.
run() ->
V = 4,
Ev = [[{1,2},{1,3},{1,4}],[{2,1},{2,3}],[{3,1},{3,2},{3,4}],[{4,1},{4,3}]] ,
R = 1,
Parent = [0,1,4,1],
step7(V,R,Ev,Parent).