我是Erlang的新手,我正在尝试了解如何将消息从一个进程发送到进程列表。
据说我们有一个数据结构,其中包含一个包含字符串和Pid的元素的列表。如何让Pid发送消息" M" Pids是前面描述的两个元素之一? 我想出的是:
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.
但据我所知,我并没有正确匹配Pids列表,因此我可以提取&#34;来自Pids列表元素的Pid,或者我没有正确使用该列表向其发送消息。
基本上,我有一个名为&#34; looper&#34;这是不断等待新消息到来。当它收到类型
的消息时{send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}
其中&#34; M&#34;是我想要广播到名为&#34; Friends&#34;的Pids列表的消息。和R只是一个整数。
R基本上是一个整数,表示消息应该走多远。
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...
在我设置Pid后,我从终端获得了什么,设置了#34;友谊&#34; Pids和广播之间的消息是:
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>}}}
当我查看广播该消息的Pid的消息时,控制台只是挂起而其他Pid没有收到消息。
非常感谢任何帮助。 感谢
答案 0 :(得分:2)
这次你得到的是if_clause
错误。在Erlang中,每个表达式都必须返回一些值if
。这意味着你可以编写像这样的代码
SomeVar = if
R =< 0 ->
[...]
R > 0 andalso FriendTale =/= []->
[...]
end
如你所知,是否需要&#34;返回&#34;一些东西,要做到这一点,它的一个分支需要运行。或者换句话说,它的一个条款需要马赫。但在您的情况下,当R > 0
和FriendsTale =:= []
不属于它们时。因此出现运行时错误。
由于一般条款的最后一个条款保留为
_ ->
[...]
始终匹配,并避免此类错误。
在您的示例中,您根本不必使用if
。你可以做的是延长你收到一些守卫的条款
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.
如果收到receive
消息,则不必与一个子句匹配。如果没有,它只是留在消息框中(在此接收中被忽略,但可能被另一个接收)。
或者在你的逻辑上,你可以在R
本身上模式匹配
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.
为了提高可行性,您可以将R
从opque整数更改为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.
如果我理解正确looper
是代表一个&#34; person&#34;的函数。每个朋友都是存储朋友列表的进程,可以添加和删除,也可以向其他朋友发送消息。
让我们从每个函数的创建子句开始(创建过程接口)
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
其中大部分都很容易实现,比如
{add_frined, Friend} ->
looper(Name, [Friend, Friends], Messages);
所以我不会详细介绍。
进行广播的人不会改变状态,所以现在让我们写这样的东西(主要是为了便于阅读)
{broadcast_to_friends, Message} ->
handle_broadcast_to_friends(Friends, Message),
looper(Name, Friends, Messages);
并在
下实现新功能handle_broadcast_to_friends(Friends, Message) ->
[ F ! {receive_message, Message} || F <- Friends ].
现在,由于确切地知道要发送哪些原子的元组不方便,我们可以包装我们的&#34;消息接口&#34;进入&#34;功能界面&#34;。例如
add_friend(Person, Friend) ->
Person ! {add_friend, Friends}.
receive_message(Person, Message) ->
Person ! {receive_message, Message}.
我们也可以在你的逻辑实现中使用它们
handle_broadcast_to_friends(Friends, Message) ->
[ receive_message(F, Message) || F <- Friends ].
这应该让你开始在正确的轨道上。如果您需要MessageID
或类似的东西,只需扩展您的界面即可。如果你真的需要创建broadcast_to_all
,你需要考虑如何处理周围的消息,这不是一个简单的问题。
答案 1 :(得分:1)
我建议您首先减少您正在做的事情的复杂性。例如,您收到的此条件处理业务不是您的基本消息传递问题的一部分。以下是常见广播习语的基本示例,使用列表推导发送到函数bcast/2
中的pid列表:
-module(bcast).
-export([start/0]).
start() ->
Pids = [spawn(fun() -> listener() end) || _ <- lists:seq(1,3)],
Message = "This is my message.",
ok = bcast(Pids, Message),
timer:sleep(100), % Give the subordinates time to act.
[exit(P, kill) || P <- Pids],
ok.
listener() ->
receive
{bcast, Message} ->
Now = now(),
io:format(user, "~p ~p: Received: ~p~n", [self(), now(), Message]),
listener();
Any ->
io:format(user, "~p HURR! Unexpected message: ~p~n", [self(), Any]),
listener()
end.
bcast(Pids, Message) ->
[P ! {bcast, Message} || P <- Pids],
ok.
您在代码中遇到的其他问题不是真正的广播问题,而是以不熟悉的语言超越自己的问题。
这个例子是异步的(我们只是以一种方式发送消息)并且要求我们杀死我们的从属进程,因为我只编写了一个无限循环。这些方面是第一件要做的事情:如何处理消息队列(如在整个邮箱中)不仅仅是发送消息,还在循环中接收消息;并且考虑当事情进展顺利时你希望你的下属进程如何死亡(我只是在上面的例子中将它们全部谋杀)。