4

このコードは先生から教えてもらいましたが、残念ながら説明がありませんでした。クラスでこれを試したところ、却下されました。

誰かがこのコードを私に徹底的に説明してくれれば、非常に役に立ちます。前もって感謝します。

-module(pingpong).
-compile(export_all).

start_pong() ->
    register(pong, spawn(pingpong,pong,[])).

pong() ->
    receive
        finished ->
            io:format("Pong finished ~n");
        {ping, Ping_Pid} ->
            io:format("i am the receiver ~n"),
        Ping_Pid ! pong,
        pong()
end.

start_ping(Pong_Node) ->
    spawn(pingpong, ping, [3, Pong_Node]).

ping(0, Pong_Node) ->
    {pong, Pong_Node} ! finished,
    io:format("Pong finished ~n");

ping(N, Pong_Node) ->
    {pong, Pong_Node} ! {ping, self()},
    receive
        pong ->
            io:format("i am the sender ~n")
    end,
    ping(N-1,Pong_Node).
4

1 に答える 1

7

最初の 2 行を見てみましょう。

-module(pingpong).
-compile(export_all).

1 つ目はモジュール宣言で、その引数はアトム(つまり、引用符なしの小文字の単語) です。Learn You Some Erlangから取得:

-module(Name).
これは常にファイルの最初の属性 (およびステートメント) であり、それには正当な理由があります: これは現在のモジュールの名前であり、Name はアトムです。これは、他のモジュールから関数を呼び出すために使用する名前です。呼び出しは、モジュール名、関数、および引数のM:F(A)形式で行われます。MFA

2 番目の文は、宣言されたすべての関数をpublicにするようコンパイラに指示します。つまり、そのモジュールに記述したすべての関数Fは、部外者が として呼び出すことができますpingpong:F
これにより、最初に学習するときのプロセスが簡素化される場合がありますが、通常は悪い習慣です。この質問を参照してください。


さっそく機能を見ていきましょう。

start_pong() ->
    register(pong, spawn(pingpong,pong,[])).

おそらく、ここからコードが開始されます。pingpong:start_pong().モジュールをコンパイルしてから、特定のマシンまたはノードの Erlang シェルを呼び出します。この関数が行うのは、 作成しようとしているプロセスの識別子としてpongという名前を登録する」spawnことだけです。

そこで、spawnErlang プロセスを作成します。spawnも組み込み関数 (BIF) であるため、そのモジュール名を先頭に追加する必要はありません。ドキュメントspawn(Module, Exported_Function, List of Arguments)に見られるように、その引数は、です。 を振り返ってみると、 「このモジュール内の関数を引数なしで実行して開始するプロセスを作成し、そのプロセスをpongと呼ぶ」だけです。
start_pongpong


pong() ->
    receive
        finished ->
            io:format("Pong finished ~n");
        {ping, Ping_Pid} ->
            io:format("i am the receiver ~n"),
        Ping_Pid ! pong,
        pong()
end.

で新しく作成されたプロセスstart_pongがこの関数を実行します。Erlang のすべてのプロセスには、独自のメールボックスがあります。プロセスは、それらのメールボックスにメッセージを残すことによって相互に通信します。メッセージはほとんど何でもかまいません。プロセス間で送信したいデータと考えてください。

新しいプロセスがreceiveステートメントに入り、メールボックスからメッセージをフェッチするか、メッセージが来るまで待機するように指示します。次に、メッセージを受信すると、パターン マッチングを使用して適切なアクションを見つけます。命令型言語に慣れている場合は、これを と考えswitchてください。それ以外の場合は、このステートメントを無視してください。

プロセスに単一の atom を含むメッセージがある場合、コンソールfinishedに出力されて終了します。アトムとプロセス識別子( pid - すべてのプロセスに 1 つある) のペアであるメッセージがプロセスにある場合、プロセスは関数の残りのコードを実行します。Pong finished
ping

大文字Ping_Pidは、Erlang に、メッセージが持っている 2 番目の値を という名前の変数に代入するように指示しますPing_Pid。たまたまpidが必要です。
このケースに入ったときに行うことは、 printi am the receiverであり、アトムを含むメッセージをpongによって識別されるプロセスに送信します。Ping_Pidこれが!オペレーターの目的です。最後に、関数は自分自身を呼び出して、メールボックスをもう一度調べます。


コンソール (おそらく別のノード/マシン) に次に書き込むのは、 への呼び出しstart_pingです。

start_ping(Pong_Node) ->
    spawn(pingpong, ping, [3, Pong_Node]).

前に見たように、ここで行うことはすべて、関数を実行するプロセスを作成することですping。引数3Pong_Node、最初のプロセスが実行されているマシン (ノード) を受け取ります。


ping(0, Pong_Node) ->
    {pong, Pong_Node} ! finished,
    io:format("Pong finished ~n");

ping(N, Pong_Node) ->
    {pong, Pong_Node} ! {ping, self()},
    receive
        pong ->
            io:format("i am the sender ~n")
    end,
    ping(N-1,Pong_Node).

この関数は 2 つのケースで定義されます (最初のブロックは -ではなく でping終わることに注意してください。これは、関数を定義する必要があることを Erlang に伝えます)。;.

3最初の引数として呼び出します。3は と一致しないため0、プロセスはN引数として で 2 番目のケースを実行します。

このプロセスは、構文 に従う で{ping, self()}指定されたプロセスにペアを送信します。現在のプロセス自身のpidを取得するために使用されます。 この後、プロセスは応答を待ち、これを繰り返します。while は 0 より大きくなります。{pong, Pong_Node}{registered_name, node_name}self()
pongN

ゼロにN達すると、最初のケースが実行され、 に送信され、finished実行が{pong, Pong_Node}終了します。


この説明が不完全であると思われる場合は、この正確なプログラムを示しているチュートリアルも参照してください。

于 2013-09-29T09:52:22.277 に答える