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() 函数来处理部分问题。