3

Erlang は初めてで、あるプロセスからプロセスのリストにメッセージを送信する方法を理解しようとしています。

文字列と Pid を含む要素を持つリストを保持するデータ構造があるとします。前に説明した2つの要素の1つであるPidにメッセージ「M」をPidに送信させるにはどうすればよいですか? 私が思いついたのは次のとおりです。

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.

しかし、私が理解していることは、Pid のリストの要素から Pid を「抽出」できるように、Pid のリストを正しくパターン一致させていないか、リストを正しく使用してメッセージを送信していないことです。それ。

基本的に、新しいメッセージが到着するのを常に待っている「ルーパー」と呼ばれる機能があります。タイプのメッセージを受信したとき

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

ここで、「M」は「Friends」と呼ばれる 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({"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 にはメッセージが受信されません。

どんな助けでも大歓迎です。ありがとう

4

2 に答える 2

1

エラーメッセージ

今回はif_clauseエラーになります。Erlang では、すべての式が何らかの値を返さなければなりませんif。このようなコードを書くことができるという意味

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

ご覧のとおり、何かを「返す」必要がある場合は、そのブランチの 1 つを実行する必要があります。または、言い換えれば、その句の 1 つが一致する必要があります。しかし、あなたの場合、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、1 つの句に一致する必要はありません。そうでない場合は、メッセージ ボックスに残されます (この受信では無視されますが、別の受信者にキャッチされる可能性があります)。

または、ロジックをたどって、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そして、readability を高めるために、opque 整数から miningfull atom に変更できます。

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いるのは、1人の「人」を表す関数です。各フレンドは、フレンドのリストを保存し、そこから追加および削除したり、別のフレンドにメッセージを送信したりできるプロセスです。

これらの各関数の句を作成することから始めましょう (プロセス インターフェイスの作成)

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 ].

ここで、送信するアトムとどのタプルを正確に知るのは便利ではないため、「メッセージ インターフェイス」を「関数インターフェイス」にラップすることができます。例えば

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必要がある場合は、周りを回るメッセージをどのように処理するかを考える必要がありますが、これは単純な問題ではありません。

于 2014-10-05T13:00:29.053 に答える