差分ok和end[Erlang]

差分ok和end[Erlang],erlang,Erlang,在Erlang中用end和ok结束函数有什么区别?我一直在努力理解以下代码中的含义: -module(esOne). -export([start/1, func/1]). start(Par) -> io:format("Client: I am ~p, spawned by the server: ~p~n",[self(),Par]), spawn(esOne, func, [self()]), Par ! {onPid, self()}, serv

在Erlang中用end和ok结束函数有什么区别?我一直在努力理解以下代码中的含义:

-module(esOne).
-export([start/1, func/1]).

start(Par) ->
    io:format("Client: I am ~p, spawned by the server: ~p~n",[self(),Par]),
    spawn(esOne, func, [self()]),
    Par ! {onPid, self()},
    serverEsOne ! {onName, self()},
    receiveMessage(),
    ok.

receiveMessage() ->
    receive
        {reply, N} ->
            io:format("Client: I received a message: ~p~n",[N])
    after
        5000->
            io:format("Client: I received no message, i quit~n",[])
    end.

func(Parent)->
    io:format("Child: I am ~p, spawned from ~p~n",[self(),Parent]).

此代码与另一个充当服务器的.erl文件配合使用。我只是通过分析给定的服务器文件并复制它的行为来写这篇文章。首先,我认为ok用于结束每个函数,但事实并非如此,因为我不能用ok结束receiveMessage()。然后我想我可以用end结束每个函数,但是如果我用end替换ok,start(Par)会给出一个错误。不仅如此,在服务器文件中,我还看到ok和end在函数中用于结束循环。它们的使用方式在我看来是一样的,但它们显然实现了一个独立的功能,因为一个不能被另一个替代。如能澄清,我们将不胜感激。

需要了解两点:

  • Erlang中的某些代码块类型以“end”结尾。所以如果。。。结束,
    案例。。。结束
    接收。。。[在N之后]。。。结束
    ,依此类推。当然可以使用“end”作为自己的原子来代替OK,但这不是上面发生的事情

  • Erlang中的每个函数都返回一些值。如果不明确,它将返回最后一个表达式的值。“=”运算符不像在其他语言中那样赋值给变量,而是像在数学中那样赋值给符号,这意味着重新赋值实际上是一种逻辑断言。如果断言失败,进程将抛出异常(这意味着它通常会崩溃)

当您以“ok”或任何其他原子结束某个内容时,您提供了一个将返回的已知最终值。您不必对其执行任何操作,但如果您希望调用过程断言函数已完成,或者如果发生任何异常情况,则可以:

do_stuff() ->
    ok = some_func().
而不是

do_stuff() ->
    some_func().
如果某些_func()可能有可能失败的副作用,它通常会返回
ok
{error,Reason}
(或类似的内容)。通过检查返回值是否为
ok
,我们可以防止调用进程在发生错误时继续执行。这是Erlang“让它崩溃”概念的核心。基本思想是,如果调用一个有副作用的函数,并且它执行了任何意外操作,则应该立即崩溃,因为处理坏数据比根本不处理更糟糕。主管将清理碰撞,系统将恢复到已知状态,而不是在副作用失效后处于任何随机状态

如果函数的目的是返回值,则上面位的一个变体是在元组中显示“ok”部分。例如,您可以在任何dict类型处理库中看到这一点。某些数据返回函数的返回类型为
{ok,Value}{error,reason}
而不是
Value{error,reason}
的原因是为了使模式匹配更自然

考虑以下案例条款:

case dict:find(Key, Dict) of
    {ok, Value} ->
        Value;
    {error, Reason} ->
        log(error, Reason),
        error
end.
以及:

在第一个示例中,我们首先匹配成功条件。但是,在第二个版本中,这是不可能的,因为error子句永远不会匹配;任何返回都将始终由
表示。哎呀


大多数情况下(但并非总是如此)返回值或崩溃的函数只返回值。对于纯函数来说尤其如此,它们不携带状态,只携带您传入的内容,并且没有副作用(例如,
dict:fetch/2
直接给出值,或者使调用过程崩溃,让您可以轻松选择要使用的方式)。返回值或发出错误信号的函数通常将有效响应包装在
{ok,value}
中,因此很容易匹配。

为了可读性,我对示例代码中的一些内容进行了调整。使用
io:format
时,您希望使用“~n”而不是“\n”。此外,我们通常对原子
使用下划线,比如对
变量名称使用驼峰格。这大大提高了可读性和视觉识别能力——在Erlang中,源代码高亮显示通常不那么重要。在@zxq9的最后一个示例中,您可以将子句还原为:将
{error,Reason}
放在第一位,将
值放在第二位。只要返回值的格式不能为
{error,Reason}
,它就可以工作。因此,
{ok,Value}
是一个更好的选择。是的,它允许您轻松编写
{ok,Val}=finder(Key,Struct)
,当找不到任何东西时会崩溃。@rvirding…我发现这是一件好事,但对于新手来说,这听起来可能不是一件好事@zxq9正确::-)但当你习惯了它:这是一个标准的崩溃方式,当一个函数没有返回你想要的,你匹配返回值与你想要的。缺少的是能够在比赛中有后卫。@Babyburger基本上,你做到了。但是请记住,
ok
是实际返回值,因此它更像是
返回“ok”在Java中。如果没有该值,则该值将是函数中最后一个表达式返回的值。在Erlang中执行
ok=somefun()
就像在Java中使用
if(somefun()!=“ok”){throw new SomeException;}
调用它,而在Erlang中调用
somefun(),
就像调用
somefun(),并将返回值放入垃圾箱。不管怎样,你走对了方向。玩弄一下Erlang,它会突然变得如此明显。:-)
case finder(Key, Struct) of
    Value ->
        Value;
    {error, Reason}
        log(error, Reason),
        error
end.