Erlang:做一个环
Erlang: Make a ring
我对 Erlang 很陌生(通读 "Software for a Concurrent World")。根据我的阅读,我们 link 两个进程共同构成了一个可靠的系统。
但是如果我们需要两个以上的进程,我认为我们应该将它们连接成一个环。虽然这与我的实际问题略有不同,但如果这不正确,请告诉我。
给定 PIDs
列表:
[1,2,3,4,5]
我想在 {My_Pid, Linked_Pid}
个元组的环中形成这些:
[{1,2},{2,3},{3,4},{4,5},{5,1}]
我在创建添加最终 {5,1}
元组的优雅解决方案时遇到了问题。
这是我的尝试:
% linkedPairs takes [1,2,3] and returns [{1,2},{2,3}]
linkedPairs([]) -> [];
linkedPairs([_]) -> [];
linkedPairs([X1,X2|Xs]) -> [{X1, X2} | linkedPairs([X2|Xs])].
% joinLinks takes [{1,2},{2,3}] and returns [{1,2},{2,3},{3,1}]
joinLinks([{A, _}|_]=P) ->
{X, Y} = lists:last(P)
P ++ [{Y, A}].
% makeRing takes [1,2,3] and returns [{1,2},{2,3},{3,1}]
makeRing(PIDs) -> joinLinks(linkedPairs(PIDs)).
我在查看我的 joinLinks
函数时感到畏缩 - list:last
很慢(我认为),而且它看起来不太 "functional"。
是否有更好、更惯用的解决方案?
如果其他函数式程序员(非 Erlang)偶然发现了这个问题,请 post 您的解决方案 - 概念是相同的。
将 lists:zip
与原始列表及其 'rotated' 版本一起使用:
1> L=[1,2,3].
[1,2,3]
2> lists:zip(L, tl(L) ++ [hd(L)]).
[{1,2},{2,3},{3,1}]
如果您要处理长列表,可以使用辅助函数避免创建中间列表tl(L) ++ [hd(L)]
:
1> L = lists:seq(1,5).
[1,2,3,4,5]
2> Link = fun Link([Last],First,Acc) -> lists:reverse([{Last,First}|Acc]);
Link([X|T],First,Acc) -> Link(T,First,[{X,hd(T)}|Acc]) end.
#Fun<erl_eval.42.127694169>
3> Joinlinks = fun(List) -> Link(List,hd(List),[]) end.
#Fun<erl_eval.6.127694169>
4> Joinlinks(L).
[{1,2},{2,3},{3,4},{4,5},{5,1}]
5>
But if we need more than two process, I think we should connect them
in a ring.
没有。例如,假设您要下载 10 个不同网页的文本。您可以为每个请求生成一个单独的进程,而不是发送一个请求,然后等待服务器响应,然后发送下一个请求等。每个派生进程只需要主进程的 pid,主进程在结果进来时收集结果。当派生进程从服务器得到回复时,派生进程向主进程发送一条带有结果的消息,然后终止。生成的进程没有理由相互发送消息。没有戒指。
我猜想在您的 erlang 生涯中,您不太可能创建一个进程环。
I have trouble creating an elegant solution that adds the final {5,1} tuple.
您可以创建传递给它们的其他四个进程 self()
,每个生成的进程都不同。然后,您可以创建一个单独的 create_ring()
函数分支来终止递归和 returns 最后创建的进程的 pid 到主进程:[=19=]
init(N) ->
LastPid = create_ring(....),
create_ring(0, PrevPid) -> PrevPid;
create_ring(N, PrevPid) when N > 0 ->
Pid = spawn(?MODULE, loop, [PrevPid]),
create_ring(.......).
然后,主进程可以调用(不是生成)其他进程生成的相同函数,将 create_ring()
函数返回的最后一个 pid 传递给该函数:
init(N) ->
LastPid = create_ring(...),
loop(LastPid).
这样一来,主进程会和其他进程进入同一个消息循环,主进程会在循环参数变量中保存最后一个pid来发送消息。
在erlang中,你经常会发现,当你在定义一个函数时,你无法在那个函数中做你想做的一切,所以你需要调用另一个函数来做它是什么给你带来麻烦,如果在第二个函数中你发现你不能做你需要做的一切,那么你需要调用另一个函数等。应用到上面的环问题,我发现 init()
不能'无法在一个函数中完成所有我想做的事情,所以我定义了 create_ring()
函数来处理部分问题。
我对 Erlang 很陌生(通读 "Software for a Concurrent World")。根据我的阅读,我们 link 两个进程共同构成了一个可靠的系统。
但是如果我们需要两个以上的进程,我认为我们应该将它们连接成一个环。虽然这与我的实际问题略有不同,但如果这不正确,请告诉我。
给定 PIDs
列表:
[1,2,3,4,5]
我想在 {My_Pid, Linked_Pid}
个元组的环中形成这些:
[{1,2},{2,3},{3,4},{4,5},{5,1}]
我在创建添加最终 {5,1}
元组的优雅解决方案时遇到了问题。
这是我的尝试:
% linkedPairs takes [1,2,3] and returns [{1,2},{2,3}]
linkedPairs([]) -> [];
linkedPairs([_]) -> [];
linkedPairs([X1,X2|Xs]) -> [{X1, X2} | linkedPairs([X2|Xs])].
% joinLinks takes [{1,2},{2,3}] and returns [{1,2},{2,3},{3,1}]
joinLinks([{A, _}|_]=P) ->
{X, Y} = lists:last(P)
P ++ [{Y, A}].
% makeRing takes [1,2,3] and returns [{1,2},{2,3},{3,1}]
makeRing(PIDs) -> joinLinks(linkedPairs(PIDs)).
我在查看我的 joinLinks
函数时感到畏缩 - list:last
很慢(我认为),而且它看起来不太 "functional"。
是否有更好、更惯用的解决方案?
如果其他函数式程序员(非 Erlang)偶然发现了这个问题,请 post 您的解决方案 - 概念是相同的。
将 lists:zip
与原始列表及其 'rotated' 版本一起使用:
1> L=[1,2,3].
[1,2,3]
2> lists:zip(L, tl(L) ++ [hd(L)]).
[{1,2},{2,3},{3,1}]
如果您要处理长列表,可以使用辅助函数避免创建中间列表tl(L) ++ [hd(L)]
:
1> L = lists:seq(1,5).
[1,2,3,4,5]
2> Link = fun Link([Last],First,Acc) -> lists:reverse([{Last,First}|Acc]);
Link([X|T],First,Acc) -> Link(T,First,[{X,hd(T)}|Acc]) end.
#Fun<erl_eval.42.127694169>
3> Joinlinks = fun(List) -> Link(List,hd(List),[]) end.
#Fun<erl_eval.6.127694169>
4> Joinlinks(L).
[{1,2},{2,3},{3,4},{4,5},{5,1}]
5>
But if we need more than two process, I think we should connect them in a ring.
没有。例如,假设您要下载 10 个不同网页的文本。您可以为每个请求生成一个单独的进程,而不是发送一个请求,然后等待服务器响应,然后发送下一个请求等。每个派生进程只需要主进程的 pid,主进程在结果进来时收集结果。当派生进程从服务器得到回复时,派生进程向主进程发送一条带有结果的消息,然后终止。生成的进程没有理由相互发送消息。没有戒指。
我猜想在您的 erlang 生涯中,您不太可能创建一个进程环。
I have trouble creating an elegant solution that adds the final {5,1} tuple.
您可以创建传递给它们的其他四个进程 self()
,每个生成的进程都不同。然后,您可以创建一个单独的 create_ring()
函数分支来终止递归和 returns 最后创建的进程的 pid 到主进程:[=19=]
init(N) ->
LastPid = create_ring(....),
create_ring(0, PrevPid) -> PrevPid;
create_ring(N, PrevPid) when N > 0 ->
Pid = spawn(?MODULE, loop, [PrevPid]),
create_ring(.......).
然后,主进程可以调用(不是生成)其他进程生成的相同函数,将 create_ring()
函数返回的最后一个 pid 传递给该函数:
init(N) ->
LastPid = create_ring(...),
loop(LastPid).
这样一来,主进程会和其他进程进入同一个消息循环,主进程会在循环参数变量中保存最后一个pid来发送消息。
在erlang中,你经常会发现,当你在定义一个函数时,你无法在那个函数中做你想做的一切,所以你需要调用另一个函数来做它是什么给你带来麻烦,如果在第二个函数中你发现你不能做你需要做的一切,那么你需要调用另一个函数等。应用到上面的环问题,我发现 init()
不能'无法在一个函数中完成所有我想做的事情,所以我定义了 create_ring()
函数来处理部分问题。