让我们看看前两行。
-module(pingpong).
-compile(export_all).
第一个是模块声明,它的参数是一个atom(或者,换句话说,一个小写单词,不带引号)。摘自Learn You Some Erlang:
-module(Name).
这始终是文件的第一个属性(和语句),并且有充分的理由:它是当前模块的名称,其中 Name 是一个原子。这是您将用于从其他模块调用函数的名称。调用以 M:F(A) 形式进行,其中 M 是模块名称,F 是函数,而 A 论据。
第二句告诉你的编译器让所有声明的函数public,ie,你写在那个模块上的每个函数F都可以被外人调用作为pingpong:F.
当您第一次学习时,这可能会简化流程,但这通常是一种不好的做法。参考this question。
现在让我们看看函数。
start_pong() ->
register(pong, spawn(pingpong,pong,[])).
这可能是您的代码开始的地方。您编译模块,然后在给定机器或节点的 Erlang shell 中调用pingpong:start_pong().。这个函数所做的只是“将名称pong注册为我将要创建的进程的标识符,spawn”。
所以,spawn 创建了一个 Erlang 进程。 spawn 也是一个内置函数 (BIF),因此不需要您预先添加其模块名称。它的参数是spawn(Module, Exported_Function, List of Arguments),见in the documentation。
回顾start_pong,它所做的只是“创建一个进程,该进程将通过在此模块中运行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,否则请忽略此语句。
如果进程有一个带有单个原子finished 的消息,它会在控制台中打印Pong finished 并退出。
如果进程有一条消息是一对与原子ping 和进程标识符(pid - 每个进程都有一个),那么它将执行剩余的代码函数。
大写的Ping_Pid 告诉Erlang 将消息的第二个值赋给名为Ping_Pid 的变量。碰巧您期望一个 pid。
进入这种情况时,它的作用是打印i 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).
这个函数在两种情况下定义(注意第一个ping 块以; 结尾,而不是. - 这告诉Erlang 还有更多要定义的函数)。
你用3 作为第一个参数来调用它。由于3 与0 不匹配,因此进程执行第二种情况,并以N 作为其参数。
此进程将{ping, self()} 对发送到{pong, Pong_Node} 给出的进程,该进程遵循语法{registered_name, node_name}。 self() 用于检索当前进程自己的pid。
此后,进程等待pong 响应,并再次重复此操作,而N 大于零。
当N 达到零时,执行第一个case,将finished 发送到{pong, Pong_Node},并结束执行。
如果你觉得这个解释不完整,你也可以看看at the tutorial,它描述了这个确切的程序。