Как передать сообщение списку процессов в Erlang?

Я новичок в Erlang и пытаюсь понять, как отправить сообщение от одного процесса к списку процессов.

Предположительно у нас есть структура данных, содержащая список с идентификаторами. Как я могу заставить Pid отправлять сообщение «M» в список Pid, ​​где каждый элемент списка имеет 2 элемента: строку (представляющую имя) и Pid? Я придумал:

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

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.

Но насколько я понимаю, я не могу правильно сопоставить шаблон со списком Pid, ​​чтобы я мог «извлечь» Pid из элемента списка Pid.

По сути, у меня есть функция под названием «looper», которая постоянно ожидает поступления новых сообщений. Когда он получает сообщение типа

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

где «M» - это сообщение, которое я хочу передать списку Pid-идентификаторов под названием «Друзья», а 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 и установлю "дружбу" между Pid:

1> f().
ok
2> c(facein).
facein.erl:72: Warning: variable 'From' is unused
{ok,facein}
3> {Message, Pid} = facein:start({"Bjarki", [], []}).
{ok,<0.186.0>}
4> {Message, Pid2} = facein:start({"Bjarki2", [], []}).
{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>}}}

Любая помощь будет принята с благодарностью. Спасибо


person sokras    schedule 04.10.2014    source источник
comment
не могли бы вы добавить loop определение (точнее, как названы ваши аргументы).   -  person mpm    schedule 04.10.2014
comment
@mpm ok добавил определение петлителя   -  person sokras    schedule 04.10.2014
comment
Вы можете использовать gproc, у него есть удобный Модуль Pub / Sub, который позволяет вам подписывать процессы на событие, а затем публиковать сообщение для всех подписчиков. мероприятия.   -  person Eric    schedule 07.10.2014


Ответы (1)



ИЗМЕНИТЬ

После того, как вы добавили функцию brodcast. То, что вы получаете, отправляя looper funciton, - это friends атом. Вы не можете выполнять составление списка по атому, только по списку. Вот почему вы получаете bedarg, когда пытаетесь использовать оператор <-.

Чтобы нарушить вашу логику: вы отправляете свой pid и атом себе, просто чтобы получить одну строку позже. Не знаете, зачем вам это нужно? Вы могли бы просто пойти в основном с тем же:

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

И теперь вы можете ясно видеть, что это атом, а не список pid-идентификаторов, который вы отправляете на looper.


Возникающая ошибка. Предполагается, что вы вызываете встроенную функцию Erlang (+ !) с неверным типом. Поэтому я предполагаю, что один из Friends не является процессом, или R не является чем-то, что вы можете - 1 использовать. Может быть, попробуйте распечатать их перед составлением списка для отладки.

Вы также можете использовать таких охранников, как

receive
  {From, {send_message, {M, R, ID, Friends}}} when is_integer(R) ->
     %%  ...

но вы просто проигнорируете сообщения, которые не соответствуют шаблону.

второстепенные примечания

Я не уверен, что вы пытаетесь это сделать, но это тоже может помочь.

Я могу заметить одну вещь: вы отправляете кортеж {send_message, {M, R-1, ID, Friends}}. Это все ваше сообщение, и только оно будет получено. Erlang не добавит ничего волшебным образом, поэтому, если вы рассчитываете получить {From, {send_message, {M, R, ID, Friends}}}, вам нужно отправить это From самостоятельно. Как это F ! {self(), {send_message, {M, R-1, ID, Friends}}}

Другая вещь, на которую вы можете обратить внимание, - это сопоставление с образцом в «более длинной» функции. Friends переменная привязывается (присваивается значению) как аргумент функции. Итак, когда вы делаете свой receive {From, {send_message, {M, R, ID, Friends}}}, то, что вы делаете, - это сопоставление с образцом для типа сообщения (два кортежа, два кортежа и четыре кортежа), список атомов send_message и Friends. Это означает, что вы будете выполнять свою «логику отправки» только тогда, когда получите список друзей точно такой же, с которым изначально была вызвана функция loop. И все остальные сообщения (кроме terminate, конечно) будут проигнорированы (просто останутся в вашем окне сообщений). Если вы рассчитываете на получение новых друзей, сопоставление с образцом для несвязанных переменных (краткость функции помогает с этим Erlang-gotcha).

person mpm    schedule 04.10.2014
comment
спасибо за ваш ответ, но я отредактировал свой вопрос, чтобы было понятнее :) Надеюсь, это поможет. Спасибо за уделенное время. - person sokras; 05.10.2014
comment
Я добавил свой полный лупер на случай, если что-то не так в какой-то другой его части. Я также лучше объясню, что такое каждая переменная. :) - person sokras; 05.10.2014