【问题标题】:Sending messages in circles.在圈子中发送消息。
【发布时间】:2014-10-18 23:49:57
【问题描述】:

我是函数式编程的新手,刚从 haskell(不太喜欢)切换到 erlang(非常喜欢)。当我作为一名自学者学习时,我偶然发现了these Exercises 并开始学习它们。

我就这个问题来了:

  1. 编写一个启动2个进程的函数,并发送一条消息M 时间在他们之间向前和向后。消息有后 已发送进程应正常终止。

我这样解决了它并且它有效(也许它可以做得更好;任何评论都非常感谢):

-module (concur).
-export ( [pingpong/1, pingpong/2] ).

pingpong (Msg, TTL) ->
    A = spawn (concur, pingpong, ["Alice"] ),
    B = spawn (concur, pingpong, ["Bob"] ),
    B ! {A, TTL * 2, Msg}.

pingpong (Name) ->
    receive
        {From, 1, Msg} -> 
            io:format ("~s received ~p and dying.~n", [Name, Msg] ),
            exit (From);
        {From, TTL, Msg} ->
            io:format ("~s received ~p.~n", [Name, Msg] ),
            From ! {self (), TTL - 1, Msg},
            pingpong (Name)
    end.

真正的问题是下一个练习:

2) 编写一个函数,在一个环中启动 N 个进程,并发送一个 消息 M 次围绕环中的所有进程。之后 消息已发送,进程应正常终止。

由于我没有将消息发送回其发起者,而是发送到链中的下一个节点,因此我必须以某种方式将接收者的进程传递给发送进程。所以我想象这个函数看起来像这样:

pingCircle (Name, Next) ->
...
    receive {TTL, Msg} -> Next ! {TTL - 1, Msg}
...

但是我该如何开始这整个事情。当我在圈子中生成第一个函数时,我仍然没有生成下一个节点,因此我不能将它作为参数传递。所以我幼稚的方法不起作用:

First = spawn (concur, pingCirle, ["Alice", Second] ),
Second = spawn (concur, pingCirle, ["Bob", Third] ),
...

同样,将下一个节点的 spawn 调用作为参数递归传递给它的前任的方法,也不能解决如何关闭圆圈的问题,即将最后一个节点传递给第一个节点。

问题是: 如何建立这个圈子?

编辑:

感谢您的出色回答,我成功实现了我的意图。至此这个问题就解决了。

一种可能的解决方案是:

-module (concur).
-export ( [pingCircle/3, pingCircle/2] ).

pingCircle (Names, Message, TTL) ->
    Processes = lists:map (fun (Name) -> spawn (?MODULE, pingCircle, [Name, nobody] ) end, Names),
    ProcessPairs = lists:zip (Processes, rot1 (Processes) ),
    lists:map (fun ( {Process, Recipient} ) -> Process ! {setRecipient, Recipient} end, ProcessPairs),
    Circle = lists:map (fun ( {Process, _} ) -> Process end, ProcessPairs),
    hd (Circle) ! {Message, TTL - 1, lists:last (Circle) }.

rot1 ( [] ) -> [];
rot1 ( [Head | Tail] ) -> Tail ++ [Head].

pingCircle (Name, Recipient) ->
    receive
        {setRecipient, NewRecipient} ->
            pingCircle (Name, NewRecipient);
        {Message, 0, Originator} ->
            io:format ("~s received ~p with TTL 0 and dying.~n", [Name, Message] ),
            if
                Originator == self () -> io:format ("All dead.~n");
                true -> Recipient ! {Message, 0, Originator}
            end;
        {Message, TTL, Originator} ->
            io:format ("~s received ~p with TTL ~p.~n", [Name, Message, TTL] ),
            if
                Originator == self () -> Recipient ! {Message, TTL - 1, Originator};
                true -> Recipient ! {Message, TTL, Originator}
            end,
            pingCircle (Name, Recipient)
    end.

这是我的peer review link

【问题讨论】:

  • +1 只是为了自学。顺便说一句,如果您想对已经正确的代码提出建设性的批评,您可以随时尝试codereview.stackexchange.com
  • +1 评论 - 这很酷,感谢您的信息!
  • 我也+1。这将有助于处理这种新语言。

标签: concurrency erlang


【解决方案1】:

这个练习已经成为所有 erlang 程序员的必修课。我给出了一个可行的解决方案here,以及一个可能有用的解释。

【讨论】:

  • +1 非常酷。当我经历这些时,我本可以使用它!
  • 非常感谢。但是在阅读了“通过仪式”这个词之后,我不敢看你的解决方案,但是在@Michael 的输入下,我自己通过了仪式。我更新了我的帖子并将我的代码上传到 codereview.stackexchange.com,非常感谢您的 cmets。
  • 欢迎您 - 并祝贺您完成了练习。考虑到您对这门语言的了解程度,我对您所写的内容印象深刻。总体而言,您的解决方案看起来不错 - 如果我有特定的 cmets,我会将它们发布在 codereview 上。大多数解决方案与我的主要区别在于,我解决了一个稍微困难的问题,即让环自创。我认为我自己的解决方案可以在可读性部门使用一些帮助。如果我想出更好的东西,我会在这里发布。
【解决方案2】:

首先生成它们,然后向它们发送开始信号。

在所有进程都已运行后发送启动信号。

【讨论】:

【解决方案3】:

这里已经有人提出了答案 -> http://simplehappy.iteye.com/?show_full=true

【讨论】:

  • 非常感谢,但经过@Michael 的投入,我更愿意继续独自尝试。
【解决方案4】:

我的回答。

-module(con_test).

start_ring(Msg, M, N) ->
    [First|_]=Processes=[spawn(?MODULE, ring, []) || _ <- lists:seq(1,N)],
    First ! {rotLeft(Processes), {Msg, M*N}}.

ring() ->
    receive
        {_List, {Msg, Count}} when Count == 0 ->
            io:format("~p got ~s. Enough! I'm out.~n", [self(), Msg]),
            exit(normal);
        {[Next|_] = List, {Msg, Count}} when Count > 0 ->
            io:format("~p got ~s. Passing it forward to ~p.~n", [self(), Msg, Next]),
            Next ! {rotLeft(List), {Msg, Count-1}},
            ring()
    after 1000 ->
        io:format("~p is out.~n", [self()]),
        exit(normal)
    end.

rotLeft([])     ->  [];
rotLeft([H|T])  ->  T ++[H].

【讨论】:

    猜你喜欢
    • 2015-03-25
    • 2011-10-17
    • 2019-07-23
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-07
    相关资源
    最近更新 更多