【问题标题】:Erlang: Make a ring二郎:做个戒指
【发布时间】:2019-04-15 23:43:34
【问题描述】:

我对 Erlang 很陌生(阅读“并发世界的软件”)。根据我的阅读,我们将两个进程连接在一起以形成一个可靠的系统。

但如果我们需要两个以上的进程,我认为我们应该将它们连接成一个环。虽然这与我的实际问题略有相干,但如果这不正确,请告诉我。

给定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 很慢(我认为),而且看起来不是很“实用”。

有没有更好、更惯用的解决方案?

如果其他函数式程序员(非 Erlang)偶然发现此问题,请发布您的解决方案 - 概念相同。

【问题讨论】:

    标签: functional-programming erlang


    【解决方案1】:

    但是如果我们需要两个以上的进程,我认为我们应该将它们连接起来 围成一圈。

    没有。例如,假设您要下载 10 个不同网页的文本。您可以为每个请求生成一个单独的进程,而不是发送请求,然后等待服务器响应,然后发送下一个请求等。每个派生进程只需要主进程的pid,主进程在结果进来时收集结果。当一个派生进程得到服务器的回复时,派生进程向主进程发送一个带有结果的消息,然后终止。衍生的进程没有理由相互发送消息。没有响铃。

    我猜你不太可能在你的 erlang 职业生涯中创建一系列流程。

    我无法创建一个优雅的解决方案来添加最终的 {5,1} 元组。

    您可以创建传递self() 的其他四个进程,每个生成的进程都将有所不同。然后,您可以创建一个单独的 create_ring() 函数分支,终止递归并将最后创建的进程的 pid 返回到主进程:

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

    【讨论】:

      【解决方案2】:

      如果您正在处理长列表,您可以避免使用辅助函数创建中间列表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>
      

      【讨论】:

        【解决方案3】:

        lists:zip 与原始列表及其“旋转”版本一起使用:

        1> L=[1,2,3].
        [1,2,3]
        2> lists:zip(L, tl(L) ++ [hd(L)]).
        [{1,2},{2,3},{3,1}]
        

        【讨论】:

        • 啊,这是旋转它的好方法——我正在寻找rotate 函数。干杯!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-13
        • 2010-11-02
        • 2010-10-02
        • 2011-03-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多