process - 如何在 Erlang 中多次从生成的进程中接收价值?
问题描述
我想通过“接收”从我调用的函数接收多个值。一半的代码如下所述:
-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。(需要注意的是,已通过传递参数 Ev 和 P 调用了 step13 函数)
step13 函数执行以下操作:它将 Ev 的 Pth 索引替换为接收到的元组 {V,V}。
现在,这里是转折点。由于 Parent 中有重复项,我们可以看到两个 1,因此收到的值仅针对 1 之一,因为由于自递归调用 step7(V-1,...),step7 循环已经向前移动。
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。
在每次调用时,您都会生成将发送回消息 {Edgev} 的流程 step13。每个 step13 进程将返回一个且只有一个消息,没有任何条件。
在 step7 中,启动 step13 流程后,您有两个分支:
- 如果 V 在父节点中(仅当 V 为 4 或 1 时才会发生),您将进入接收块等待消息。
- 否则你什么都不做
- 项目清单
在第一次调用时
V == 4
,您会收到来自 step13 进程的消息,阅读它并将其从邮箱中删除在下次通话时
V == 3
,您会收到来自 step13 流程的消息,但您没有阅读它,因此它保留在邮箱中。下次通话时同样的事情
V == 2
接下来,
V == 1
您收到来自 step13 过程的消息,读取邮箱,您会得到 V 等于 3 时发送的消息。为 V = 2 和 1 发送的消息仍在邮箱中。最后,在下一次调用时,
V == 0
您的程序退出,邮箱仍然是满的。
我在您的代码中看到以下问题
该条件(lists:member(V,Parent) == true)
仅适用于 V ==4 和 V ==1。您只能收到 2 条消息,但您希望收到 3 条消息。
当您将接收块置于条件下时,您会在邮箱中留下不需要的消息,稍后将替换预期的消息(我不明白您要做什么,但这是我的猜测)您应该首先收到消息然后执行测试,或者,添加对调用的引用,在返回消息中将其发回并有条件地接收带有此引用的消息(我认为这是一个不好的解决方案,因为它不会清空邮箱)。
Step7 启动一个进程并等待它的回答。除非真正的代码应该在两者之间处理其他数据,否则它是没有用的。一个简单的调用会更有效率。
[编辑]
根据您的评论并阅读您的代码,我做了第一个假设
- 您只想在 V = 4,3,2 时处理函数 step13
- 收集所有结果
如果这是正确的,我在您的代码中进行了第一次修改,其中 step7 有 3 个不同的步骤
- 产生所有 step13 进程
- 收集所有回复
- 返回结果
这是逻辑顺序,但通常,这些步骤在代码中以相反的顺序出现:
-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).
推荐阅读
- python-3.x - python aws botocore.response.streamingbody 到 json
- python - 是否可以在没有深拷贝的情况下用另一个数组的视图替换 numpy 数组的内容?
- php - Woocommerce 产品导出:使用函数 add_export_data( $value, $product ) 添加多个元字段
- performance - CreateJS CDN 性能问题
- javascript - 如何让这个函数返回图像?
- asp.net - 在asp.net c#中将字节转换为图像
- c# - Asp.Net Core 3.1 控制器方法未调用
- api - 如何在 azure api 管理服务中自动导入 API
- spring-boot - 从 javascript 访问 html 模板
- reactjs - Mixins 在 create-react-app 中不能与 dart-sass 一起使用