最初の 2 行を見てみましょう。
-module(pingpong).
-compile(export_all).
1 つ目はモジュール宣言で、その引数はアトム(つまり、引用符なしの小文字の単語) です。Learn You Some Erlangから取得:
-module(Name).
これは常にファイルの最初の属性 (およびステートメント) であり、それには正当な理由があります: これは現在のモジュールの名前であり、Name はアトムです。これは、他のモジュールから関数を呼び出すために使用する名前です。呼び出しは、モジュール名、関数、および引数のM:F(A)
形式で行われます。M
F
A
2 番目の文は、宣言されたすべての関数をpublicにするようコンパイラに指示します。つまり、そのモジュールに記述したすべての関数F
は、部外者が として呼び出すことができますpingpong:F
。
これにより、最初に学習するときのプロセスが簡素化される場合がありますが、通常は悪い習慣です。この質問を参照してください。
さっそく機能を見ていきましょう。
start_pong() ->
register(pong, spawn(pingpong,pong,[])).
おそらく、ここからコードが開始されます。pingpong:start_pong().
モジュールをコンパイルしてから、特定のマシンまたはノードの Erlang シェルを呼び出します。この関数が行うのは、 「作成しようとしているプロセスの識別子としてpongという名前を登録する」spawn
ことだけです。
そこで、spawn
Erlang プロセスを作成します。spawn
も組み込み関数 (BIF) であるため、そのモジュール名を先頭に追加する必要はありません。ドキュメントspawn(Module, Exported_Function, List of Arguments)
に見られるように、その引数は、です。
を振り返ってみると、 「このモジュール内の関数を引数なしで実行して開始するプロセスを作成し、そのプロセスをpongと呼ぶ」だけです。
start_pong
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_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
。引数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).
この関数は 2 つのケースで定義されます (最初のブロックは -ではなく でping
終わることに注意してください。これは、関数を定義する必要があることを Erlang に伝えます)。;
.
3
最初の引数として呼び出します。3
は と一致しないため0
、プロセスはN
引数として で 2 番目のケースを実行します。
このプロセスは、構文 に従う で{ping, self()}
指定されたプロセスにペアを送信します。現在のプロセス自身のpidを取得するために使用されます。
この後、プロセスは応答を待ち、これを繰り返します。while は 0 より大きくなります。{pong, Pong_Node}
{registered_name, node_name}
self()
pong
N
ゼロにN
達すると、最初のケースが実行され、 に送信され、finished
実行が{pong, Pong_Node}
終了します。
この説明が不完全であると思われる場合は、この正確なプログラムを示しているチュートリアルも参照してください。