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

让我们看一下前两行。

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

第一个是模块声明,它的参数是一个原子(或者,换句话说,一个小写单词,不带引号)。取自Learn You Some Erlang

-module(Name).
这始终是文件的第一个属性(和语句),并且有充分的理由:它是当前模块的名称,其中 Name 是一个原子。这是您将用于从其他模块调用函数的名称。调用是使用M:F(A)表单进行的,其中M是模块名称、F函数和A参数。

第二句告诉您的编译器将所有声明的函数公开您在该模块上编写的每个函数F都可以被外部人员调用为pingpong:F.
当您第一次学习时,这可能会简化流程,但这通常是一种不好的做法。参考这个问题


现在让我们看一下这些功能。

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

这可能是您的代码开始的地方。您编译模块,然后调用pingpong:start_pong().给定机器或节点的 Erlang shell。这个函数所做的只是“将名称pong注册为我将要创建的进程的标识符,使用spawn

因此,spawn创建了一个 Erlang 进程。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 - 每个进程都有一个)成对的消息,那么它将执行函数的剩余代码。Pong finished
ping

大写字母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函数的进程,带有参数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).

这个函数在两种情况下定义(注意第一个ping块以 结尾;,而不是.- 这告诉 Erlang 还有更多要定义的函数)。

您将其3称为第一个参数。由于3与 不匹配0,该过程执行第二种情况,并N作为其参数。

此过程将该对发送{ping, self()}到由 给出的过程{pong, Pong_Node},该过程遵循语法{registered_name, node_name}self()用于检索当前进程自己的pid
在此之后,该过程等待pong响应,并再次重复此操作,whileN大于零。

N达到零时,第一个案例被执行,发送finished{pong, Pong_Node},并结束执行。


如果你觉得这个解释不完整,你也可以看看教程,它描述了这个确切的程序。

于 2013-09-29T09:52:22.277 回答