如何将消息广播到Erlang的进程列表?控制台挂 [英] How to broadcast a message to a list of processes in Erlang? Console hanging

查看:269
本文介绍了如何将消息广播到Erlang的进程列表?控制台挂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Erlang的新手,我正在努力了解如何将消息从一个进程发送到进程列表。



假设我们有一个数据结构,其中包含包含字符串和Pid的元素的列表。如何使Pid发送消息M到以前描述的两个元素之一的Pids?
我想出的是:

 广播(P,M,R) - > 
P! {self(),friends},
收到
{P,朋友} - >
P! {self(),{send_message,{M,R,P,Friends}}}
end。

looper({Name,Friends,Messages}) - >
收到
{From,friends} - >
从! {self(),Friends},
looper({Name,Friends,Messages});
{From,{send_message,{M,R,ID,[{FriendPid,FriendName} | FriendTale]}}} - >
如果R =< 0 - >
从! {From,{self(),{ID,M}}},
looper({Name,[{FriendPid,FriendName} | FriendTale],[{ID,M} | Messages]})
R> 0 andalso FriendTale = / = [] - >
FriendPid! {From,{send_message,{M,R-1,ID,FriendTale}}},
looper({Name,FriendTale,[{ID,M} | Messages]})
end;
终止 - >
ok
结束。

但是从我所了解的是,我没有正确匹配Pids列表,以便我可以从Pids列表的元素中提取Pid,否则我没有正确使用该列表来发送消息。



基本上,我有一个不断等待新消息到达的称为活套的功能。当收到类型为

的消息

  {send_message,{M,R,ID,[{FriendPid,FriendName} | FriendTale]}} 

其中M是要广播到Pids列表的消息称为好友,R只是一个整数。



R基本上是一个整数,表示消息应该走多远。

  eg 0 =将消息广播到自己,
1 =广播消息给pid的朋友,
2 =广播消息给朋友的pid等等...

在设置Pid后,从终端得到的东西,设置ids之间的友谊并广播消息是:

  1> F()。 
ok
2> C(facein)。
facein.erl:72:警告:变量'From'未使用
{ok,facein}
3> {Message,Pid} = facein:start({dummy,[],[]})。
{ok,< 0.186.0>}
4> {Message,Pid2} = facein:start({dummy2,[],[]})。
{ok,< 0.188.0>}
5> facein:add_friend(PID,PID2)。
ok
6> facein:广播(PID, 你好,1)。
= ERROR REPORT ==== 2014年10月5日:: 12:12:58 ===
错误进程< 0.186.0>退出值:{if_clause,[{facein,looper,1,[{file,facein.erl},{line,74}]}]}

{< 0.177.0> ,{send_message,{hello,1,#Ref< 0.0.0.914>}}}

当我查看广播消息的Pid的消息时,控制台刚挂起,其他Pid没有收到消息。



任何帮助将不胜感激。
谢谢

解决方案

错误信息



你得到的是 if_clause 错误。在Erlang中,每个表达式都必须返回一些值,如果包含。意思是你可以编写这样的代码

  SomeVar = if 
R = 0 - >
[...]
R> 0 andalso FriendTale = / = [] - >
[...]
end

如你所见, 返回一些,要做到这一点,其中一个分支需要运行。或者换句话说,它的一个条款需要加速。但在你的情况下,当 R> 0 FriendsTale =:= [] 不是。因此,运行时错误。



由于一般实践最后的条款留作为

 code> _  - > 
[...]

这将始终匹配,并保存您的这种错误。



在您的示例中,您根本不必使用,如果。你可以做的是延长你收到的条款与一些卫兵

  looper({Name,Friends,Messages}) - > 
收到
{From,{send_message,{M,R,ID,[{FriendPid,FriendName} | FriendTale]}}}
当R =< 0 - >
从! {From,{self(),{ID,M}}},
looper({Name,[{FriendPid,FriendName} | FriendTale],[{ID,M} | Messages]})
{From,{send_message,{M,R,ID,[{FriendPid,FriendName} | FriendTale]}}}
当R> 0 andalso FriendTale = / = [] - >
FriendPid! {From,{send_message,{M,R-1,ID,FriendTale}}},
looper({Name,FriendTale,[{ID,M} | Messages]})
终止 - >
ok
结束。

如果收到收到的消息不需要匹配一个子句。如果没有,它只是留在消息框(忽略在这个接收,但可以被另一个捕获)。



或者让你的逻辑你可以模式匹配在 R 本身

  looper({Name,Friends,Messages}) - > 
收到
{From,{send_message,{M,0,ID,[{FriendPid,FriendName} | FriendTale]}}} - >
从! {From,{self(),{ID,M}}},
looper({Name,[{FriendPid,FriendName} | FriendTale],[{ID,M} | Messages]})
{From,{send_message,{M,1,ID,[{FriendPid,FriendName} | FriendTale]}}}
当FriendTale = / = [] - >
FriendPid! {From,{send_message,{M,R-1,ID,FriendTale}}},
looper({Name,FriendTale,[{ID,M} | Messages]})
终止 - >
ok
结束。

为了提高准备性,您可以更改 R 从opque整数到采矿原子

  looper({Name,Friends,Messages}) - > 
收到
{From,{send_message,{M,back_to_me,ID,[{FriendPid,FriendName} | FriendTale]}}} - >
从! {From,{self(),{ID,M}}},
looper({Name,[{FriendPid,FriendName} | FriendTale],[{ID,M} | Messages]})

{From,{send_message,{M,to_friends,ID,[{FriendPid,FriendName} | FriendTale]}}}
当FriendTale = / = [] - >
FriendPid! {From,{send_message,{M,R-1,ID,FriendTale}}},
looper({Name,FriendTale,[{ID,M} | Messages]})

终止 - >
ok
结束。



广播给朋友



如果我明白正确 looper 是代表一个人的功能。每个朋友都是存储朋友列表的过程,可以添加和删除它,并且可以向另一个朋友发送消息。



让这些函数从创建子句开始(创建过程界面)

  looper(名称,朋友,消息) - > 
收到
{add_friend,Friend} - >
[...]
{remove_friend,Friend} - >
[...]
{receive_message,Message} - >
[...]; frineds
{broadcast_to_self,Message} - >
[...]
{broadcast_to_friends,Message} - >
[...]
终止 - >
ok
end

大多数都很容易实现,如

  {add_frined,Friend}  - > 
looper(姓名,[朋友,朋友],消息);

所以我不会详细介绍。



播放的那些不会改变状态,所以现在可以写这样的东西(主要是为了可读性

  {broadcast_to_friends,Message}  - > 
handle_broadcast_to_friends(Friends,Message),
looper(Name,Friends,Messages);

并在

  handle_broadcast_to_friends(朋友,消息)之下实现新功能 - > 
[F!{receive_message,Message} || F < - Friends]。

现在,由于知道要发送哪个原子的元组是不方便的,所以我们可以将message interface包装到funciton interface中,例如

  add_friend(Person,Friend) - > 
个人!{add_friend,Friends}

receive_message(Person,Message) - >
个人! {receive_message,Message}。

我们也可以在你的逻辑实现中使用这些。

  handle_broadcast_to_friends (朋友,留言) - > 
[receive_message(F,Message)|| F < - 朋友]。

应该开始你在wright轨道。如果您需要 MessageID 或类似的东西,只需扩展您的界面。如果您真的需要创建 broadcast_to_all ,那么您需要考虑如何处理围绕的消息,这是一个不简单的问题。


I am new to Erlang and I am trying to understand how to send a message from one process to a list of processes.

Supposedly we have a data structure that holds a list with elements containing a string and Pid. How can I make a Pid send a message "M" to Pids that are one of the two elements previously described ? What I have come up with is:

broadcast(P, M, R) ->
  P ! {self(), friends},
  receive
    {P, Friends} ->
  P ! {self(), {send_message, {M, R, P, Friends}}}
  end.

looper({Name, Friends, Messages}) ->
receive
  {From, friends} ->
    From ! {self(), Friends},
    looper({Name, Friends, Messages});
  {From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
    if R =< 0 ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
       R > 0  andalso FriendTale =/= []->
         FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
         looper({Name, FriendTale, [{ID, M} | Messages]})
    end;
   terminate ->
    ok
end.

But from what I understand is that I don't pattern match correctly the list of Pids so that I can "extract" the Pid from an element of the list of Pids, or I don't correctly use the list to send a message to it.

Basically, I have a function called "looper" that is constantly waiting for new messages to arrive. When it receives a message of type

{send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}

where "M" is the message that I want to broadcast to the list of Pids called "Friends" and R is just an integer.

The R is basically an integer saying how far the message should go.

e.g. 0 = broadcast the message to self,
     1 = broadcast the message to the friends of the pid,
     2 = broadcast the message to the friends of the friends of the pid and so on...

What I get from the terminal after I setup the Pid, set the "friendships" between the Pids and broadcast a message is:

1> f().
ok
2> c(facein).
facein.erl:72: Warning: variable 'From' is unused
{ok,facein}
3> {Message, Pid} = facein:start({"dummy", [], []}).
{ok,<0.186.0>}
4> {Message, Pid2} = facein:start({"dummy2", [], []}).
{ok,<0.188.0>}
5> facein:add_friend(Pid,Pid2).
ok
6> facein:broadcast(Pid,"hello",1).    
=ERROR REPORT==== 5-Oct-2014::12:12:58 ===
Error in process <0.186.0> with exit value: {if_clause,[{facein,looper,1,[{file,"facein.erl"},{line,74}]}]}

{<0.177.0>,{send_message,{"hello",1,#Ref<0.0.0.914>}}}

When I view the messages of the Pid that broadcasted the message, then the console just hangs and the other Pids have no messages received.

Any help would be greatly appreciated. Thanks

解决方案

error message

This time what you get is if_clause error. In Erlang every expression have to return some value, if included. Meaning that you could write code like this

SomeVar = if 
     R =< 0 ->
       [...]
     R > 0 andalso FriendTale =/= []->
       [...]
  end

As you can see if needs to "return" something, and to do that, one of it's branches needs to run. Or in other words one of it's clauses needs to mach. But in your case, when R > 0 and FriendsTale =:= [] non of them does. Hence an run-time error.

As general practive last of clauses is left as

  _ ->  
     [...]

which will always match, and save you from such error.

In you example you don't have to use if at all. What you could do is to extend you receive clauses with some guards

looper({Name, Friends, Messages}) ->
  receive
    {From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}} 
       when R =< 0 ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
    {From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}}  
       when R > 0  andalso FriendTale =/= [] ->
          FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
          looper({Name, FriendTale, [{ID, M} | Messages]});
    terminate ->
          ok
  end.

In case of receive message that is received do not have to match to one clauses. And if it does not, it is just left in message box (ignored in this receive, but could be caught by another).

Or fallowing you logic you could pattern match on R itself

looper({Name, Friends, Messages}) ->
  receive
    {From, {send_message, {M, 0, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
    {From, {send_message, {M, 1, ID, [{FriendPid, FriendName} | FriendTale]}}}  
       when FriendTale =/= [] ->
          FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
          looper({Name, FriendTale, [{ID, M} | Messages]});
    terminate ->
          ok
  end.

And to increase readybility, you could change R from opque integer, to miningfull atoms

looper({Name, Friends, Messages}) ->
  receive
    {From, {send_message, {M, back_to_me, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});

    {From, {send_message, {M, to_friends, ID, [{FriendPid, FriendName} | FriendTale]}}}  
       when FriendTale =/= [] ->
          FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
          looper({Name, FriendTale, [{ID, M} | Messages]});

    terminate ->
          ok
  end.

broadcast to friends

If I understand correctly looper is function representing one "person". Each friend is process that stores list of friends, can add and remove from it, and can send messages to another friends.

Lets start from creating clause for each of those functions (creating process interface)

looper(Name, Friends, Messages) ->
  receive 
     {add_friend, Friend} ->
        [...];
     {remove_friend, Friend} ->
        [...];
     {receive_message, Message} ->
        [...];frineds
     {broadcast_to_self, Message} ->
        [...];
     {broadcast_to_friends, Message} ->
        [...];
     terminate ->
        ok
  end

Most of those are easily implemented, like

{add_frined, Friend} ->
    looper(Name, [Friend, Friends], Messages);

so I will not go into details.

The ones that do the broadcast do not change state, so for now lets write something like this (mostly for sake of readability

     {broadcast_to_friends, Message} ->
        handle_broadcast_to_friends(Friends, Message),
        looper(Name, Friends, Messages);

and implement new function below

handle_broadcast_to_friends(Friends, Message) ->
   [ F ! {receive_message, Message} || F <- Friends ].

Now, since knowing exactly what tuple with which atoms to send is not convenient, we could wrap our "message interface" into "funciton interface". For example

add_friend(Person,  Friend) ->
   Person ! {add_friend, Friends}.

receive_message(Person, Message) ->
    Person ! {receive_message, Message}.

And we could use those also in your logic implementation

handle_broadcast_to_friends(Friends, Message) ->
   [ receive_message(F, Message)  || F <- Friends ].

That should start you on wright track. If you need MessageID or something like that, just extend your interface. If you really need to create broadcast_to_all, you need to think how would you handle messages circling around, which is a not simple problem.

这篇关于如何将消息广播到Erlang的进程列表?控制台挂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆